1 19 package org.openide.text; 20 21 import java.awt.Component ; 22 import java.util.logging.Level ; 23 import java.util.logging.Logger ; 24 import org.openide.DialogDisplayer; 25 import org.openide.NotifyDescriptor; 26 import org.openide.awt.StatusDisplayer; 27 import org.openide.awt.UndoRedo; 28 import org.openide.cookies.EditorCookie; 29 import org.openide.util.Lookup; 30 import org.openide.util.NbBundle; 31 32 import org.openide.util.RequestProcessor; 34 import org.openide.util.Task; 35 import org.openide.util.TaskListener; 36 import org.openide.util.UserQuestionException; 37 import org.openide.windows.*; 38 39 import java.awt.Font ; 40 import java.awt.Toolkit ; 41 import java.awt.print.PageFormat ; 42 import java.awt.print.Pageable ; 43 import java.awt.print.Printable ; 44 import java.awt.print.PrinterAbortException ; 45 import java.awt.print.PrinterException ; 46 import java.awt.print.PrinterJob ; 47 48 import java.beans.PropertyChangeEvent ; 49 import java.beans.PropertyChangeListener ; 50 import java.beans.PropertyChangeSupport ; 51 52 import java.io.*; 53 import java.lang.ref.Reference ; 54 import java.lang.ref.WeakReference ; 55 56 import java.util.*; 57 58 import javax.swing.JButton ; 59 import javax.swing.JEditorPane ; 60 import javax.swing.SwingUtilities ; 61 import javax.swing.event.ChangeEvent ; 62 import javax.swing.event.ChangeListener ; 63 import javax.swing.event.DocumentEvent ; 64 import javax.swing.event.DocumentListener ; 65 import javax.swing.event.UndoableEditEvent ; 66 import javax.swing.text.*; 67 import javax.swing.undo.CannotRedoException ; 68 import javax.swing.undo.CannotUndoException ; 69 import javax.swing.undo.UndoableEdit ; 70 import org.netbeans.api.editor.mimelookup.MimeLookup; 71 import org.netbeans.api.editor.mimelookup.MimePath; 72 import org.openide.util.Exceptions; 73 74 75 94 public abstract class CloneableEditorSupport extends CloneableOpenSupport { 95 96 public static final String EDITOR_MODE = "editor"; private static final String PROP_PANE = "CloneableEditorSupport.Pane"; private static final int DOCUMENT_NO = 0; 99 private static final int DOCUMENT_LOADING = 1; 100 private static final int DOCUMENT_READY = 2; 101 private static final int DOCUMENT_RELOADING = 3; 102 103 105 private static final ThreadLocal <Boolean > LOCAL_LOAD_TASK = new ThreadLocal <Boolean >(); 106 107 108 private static final Logger ERR = Logger.getLogger("org.openide.text.CloneableEditorSupport"); 110 111 private boolean inUserQuestionExceptionHandler; 112 113 118 private Task prepareTask; 119 120 121 private EditorKit kit; 122 123 124 private StyledDocument doc; 125 126 127 private String mimeType; 128 129 130 131 133 134 private Listener listener; 135 136 137 private UndoRedo.Manager undoRedo; 138 139 140 private Line.Set lineSet; 141 142 144 private boolean printing; 145 146 147 private final Object LOCK_PRINTING = new Object (); 148 149 150 private PositionRef.Manager positionManager; 151 152 154 155 157 158 private Set<ChangeListener > listeners; 159 160 161 private transient Reference <Pane > lastSelected; 162 163 164 private long lastSaveTime; 165 166 169 private boolean reloadDialogOpened; 170 171 172 private PropertyChangeSupport propertyChangeSupport; 173 174 175 private Lookup lookup; 176 177 178 179 private boolean alreadyModified; 181 182 188 private boolean revertingUndoOrReloading; 189 private boolean justRevertedToNotModified; 190 private int documentStatus = DOCUMENT_NO; 191 private Throwable prepareDocumentRuntimeException; 192 193 196 private Map<Line,Reference <Line>> lineSetWHM; 197 private boolean annotationsLoaded; 198 199 203 private boolean externallyModified; 204 205 210 public CloneableEditorSupport(Env env) { 211 this(env, Lookup.EMPTY); 212 } 213 214 222 public CloneableEditorSupport(Env env, Lookup l) { 223 super(env); 224 this.lookup = l; 225 } 226 227 231 236 protected abstract String messageSave(); 237 238 242 protected abstract String messageName(); 243 244 252 protected String messageHtmlName() { 253 return null; 254 } 255 256 263 protected String documentID() { 264 return messageName(); 265 } 266 267 271 protected abstract String messageToolTip(); 272 273 282 protected String messageLine(Line line) { 283 return NbBundle.getMessage(Line.class, "FMT_CESLineDisplayName", messageName(), line.getLineNumber() + 1); 284 } 285 286 290 293 final Env cesEnv() { 294 return (Env) env; 295 } 296 297 299 final EditorKit cesKit() { 300 return kit; 301 } 302 303 307 protected final synchronized UndoRedo.Manager getUndoRedo() { 308 CloneableEditorSupport redirect = CloneableEditorSupportRedirector.findRedirect(this); 309 if (redirect != null) { 310 return redirect.getUndoRedo(); 311 } 312 313 if (undoRedo == null) { 314 undoRedo = createUndoRedoManager(); 315 } 316 317 return undoRedo; 318 } 319 320 326 final synchronized PositionRef.Manager getPositionManager() { 327 if (positionManager == null) { 328 positionManager = new PositionRef.Manager(this); 329 } 330 331 return positionManager; 332 } 333 334 void ensureAnnotationsLoaded() { 335 if (!annotationsLoaded) { 336 annotationsLoaded = true; 337 338 Line.Set lines = getLineSet(); 339 for (AnnotationProvider act : Lookup.getDefault().lookupAll(AnnotationProvider.class)) { 340 act.annotate(lines, lookup); 341 } 342 } 343 } 344 345 348 private void askUserAndDoOpen(UserQuestionException e) { 349 while (e != null) { 350 NotifyDescriptor nd = new NotifyDescriptor.Confirmation( 351 e.getLocalizedMessage(), NotifyDescriptor.YES_NO_OPTION 352 ); 353 nd.setOptions(new Object [] { NotifyDescriptor.YES_OPTION, NotifyDescriptor.NO_OPTION }); 354 355 Object res = DialogDisplayer.getDefault().notify(nd); 356 357 if (NotifyDescriptor.OK_OPTION.equals(res)) { 358 try { 359 e.confirmed(); 360 } catch (IOException ex1) { 361 Exceptions.printStackTrace(ex1); 362 363 return; 364 } 365 } else { 366 return; 367 } 368 369 e = null; 370 371 try { 372 getListener().loadExc = null; 373 prepareTask = null; 374 documentStatus = DOCUMENT_NO; 375 openDocument(); 376 377 super.open(); 378 } catch (UserQuestionException ex) { 379 e = ex; 380 } catch (IOException ex) { 381 ERR.log(Level.INFO, null, ex); 382 } 383 } 384 } 385 386 388 public void open() { 389 CloneableEditorSupport redirect = CloneableEditorSupportRedirector.findRedirect(this); 390 if (redirect != null) { 391 redirect.open(); 392 return; 393 } 394 395 try { 396 if (getListener().loadExc instanceof UserQuestionException) { 397 getListener().loadExc = null; 398 prepareTask = null; 399 documentStatus = DOCUMENT_NO; 400 } 401 402 openDocument(); 403 super.open(); 404 } catch (final UserQuestionException e) { 405 if (SwingUtilities.isEventDispatchThread()) { 406 askUserAndDoOpen(e); 407 } else { 408 SwingUtilities.invokeLater( 409 new Runnable () { 410 public void run() { 411 askUserAndDoOpen(e); 412 } 413 } 414 ); 415 } 416 } catch (IOException e) { 417 ERR.log(Level.INFO, null, e); 418 } 419 } 420 421 425 430 public final void addPropertyChangeListener(java.beans.PropertyChangeListener l) { 431 getPropertyChangeSupport().addPropertyChangeListener(l); 432 } 433 434 439 public final void removePropertyChangeListener(java.beans.PropertyChangeListener l) { 440 getPropertyChangeSupport().removePropertyChangeListener(l); 441 } 442 443 449 protected final void firePropertyChange(String propertyName, Object oldValue, Object newValue) { 450 getPropertyChangeSupport().firePropertyChange(propertyName, oldValue, newValue); 451 } 452 453 private synchronized PropertyChangeSupport getPropertyChangeSupport() { 454 if (propertyChangeSupport == null) { 455 propertyChangeSupport = new PropertyChangeSupport (this); 456 } 457 458 return propertyChangeSupport; 459 } 460 461 466 473 public Task prepareDocument() { 474 CloneableEditorSupport redirect = CloneableEditorSupportRedirector.findRedirect(this); 475 if (redirect != null) { 476 return redirect.prepareDocument(); 477 } 478 synchronized (getLock()) { 479 switch (documentStatus) { 480 case DOCUMENT_NO: 481 documentStatus = DOCUMENT_LOADING; 482 483 return prepareDocument(false); 484 485 default: 486 487 if (prepareTask == null) { throw new IllegalStateException (); 489 } 490 491 return prepareTask; 492 } 493 } 494 } 495 496 498 private Task prepareDocument(final boolean notUsed) { 499 if (prepareTask != null) { 500 return prepareTask; 501 } 502 503 try { 504 env.removePropertyChangeListener(getListener()); 507 env.addPropertyChangeListener(getListener()); 508 509 kit = createEditorKit(); 512 513 if (doc == null) { 514 doc = createStyledDocument(kit); 515 516 } 520 521 final StyledDocument docToLoad = doc; 522 523 prepareTask = RequestProcessor.getDefault().post(new Runnable () { 525 private boolean runningInAtomicLock; 526 527 public void run() { 528 if (!runningInAtomicLock) { 532 runningInAtomicLock = true; 533 NbDocument.runAtomic(docToLoad, 534 this); 535 return; 536 } 537 synchronized (getLock()) { 539 if (documentStatus == 540 DOCUMENT_NO) { 541 return; 542 } 543 if (doc != docToLoad) { 545 return; 546 } 547 prepareDocumentRuntimeException = null; 548 int targetStatus = DOCUMENT_NO; 549 550 try { 551 getListener().run(); 553 documentStatus = DOCUMENT_READY; 555 fireDocumentChange(doc, 556 false); 557 targetStatus = DOCUMENT_READY; 559 doc.addUndoableEditListener(getUndoRedo()); 564 } 565 catch (DelegateIOExc t) { 566 prepareDocumentRuntimeException = t; 567 prepareTask = null; 568 } 569 catch (RuntimeException t) { 570 prepareDocumentRuntimeException = t; 571 prepareTask = null; 572 Exceptions.printStackTrace(t); 573 throw t; 574 } 575 catch (Error t) { 576 prepareDocumentRuntimeException = t; 577 prepareTask = null; 578 Exceptions.printStackTrace(t); 579 throw t; 580 } 581 finally { 582 synchronized (getLock()) { 583 documentStatus = targetStatus; 584 getLock().notifyAll(); 585 } 586 } 587 } 588 } 589 }); 590 } catch (RuntimeException ex) { 591 synchronized (getLock()) { 592 prepareDocumentRuntimeException = ex; 593 documentStatus = DOCUMENT_NO; 594 getLock().notifyAll(); 595 } 596 597 throw ex; 598 } 599 600 return prepareTask; 601 } 602 603 final void addRemoveDocListener(Document d, boolean add) { 604 if (Boolean.TRUE.equals(d.getProperty("supportsModificationListener"))) { 606 if (add) { 607 d.putProperty("modificationListener", getListener()); } else { 609 d.putProperty("modificationListener", null); } 611 } else { 612 if (add) { 613 d.addDocumentListener(getListener()); 614 } else { 615 d.removeDocumentListener(getListener()); 616 } 617 } 618 } 619 620 621 private void clearDocument() { 622 NbDocument.runAtomic( 623 doc, 624 new Runnable () { 625 public void run() { 626 try { 627 addRemoveDocListener(doc, false); 628 doc.remove(0, doc.getLength()); addRemoveDocListener(doc, true); 630 } catch (BadLocationException ble) { 631 ERR.log(Level.INFO, null, ble); 632 } 633 } 634 } 635 ); 636 } 637 638 650 public StyledDocument openDocument() throws IOException { 651 CloneableEditorSupport redirect = CloneableEditorSupportRedirector.findRedirect(this); 652 if (redirect != null) { 653 return redirect.openDocument(); 654 } 655 synchronized (getLock()) { 656 return openDocumentCheckIOE(); 657 } 658 } 659 660 private StyledDocument openDocumentCheckIOE() throws IOException { 661 StyledDocument doc = openDocumentImpl(); 662 663 IOException ioe = getListener().checkLoadException(); 664 665 if (ioe != null) { 666 throw ioe; 667 } 668 669 return doc; 670 } 671 672 675 private StyledDocument openDocumentImpl() throws IOException, InterruptedIOException { 676 switch (documentStatus) { 677 case DOCUMENT_NO: 678 documentStatus = DOCUMENT_LOADING; 679 prepareDocument(false); 680 681 return openDocumentImpl(); 682 683 case DOCUMENT_RELOADING: case DOCUMENT_READY: 685 return doc; 686 687 default: 689 try { 690 getLock().wait(); 691 } catch (InterruptedException e) { 692 throw (InterruptedIOException) new InterruptedIOException().initCause(e); 693 } 694 695 if (prepareDocumentRuntimeException != null) { 696 if (prepareDocumentRuntimeException instanceof DelegateIOExc) { 697 throw (IOException) prepareDocumentRuntimeException.getCause(); 698 } 699 700 if (prepareDocumentRuntimeException instanceof Error ) { 701 throw (Error ) prepareDocumentRuntimeException; 702 } else { 703 throw (RuntimeException ) prepareDocumentRuntimeException; 704 } 705 } 706 707 return openDocumentImpl(); 708 } 709 } 710 711 716 public StyledDocument getDocument() { 717 CloneableEditorSupport redirect = CloneableEditorSupportRedirector.findRedirect(this); 718 if (redirect != null) { 719 return redirect.getDocument(); 720 } 721 synchronized (getLock()) { 722 while (true) { 723 switch (documentStatus) { 724 case DOCUMENT_NO: 725 return null; 726 727 default: 729 if (LOCAL_LOAD_TASK.get() != null) { 730 return doc; 731 } 732 733 try { 734 return openDocumentCheckIOE(); 735 } catch (IOException e) { 736 return null; 737 } 738 } 739 } 740 } 741 } 742 743 747 public boolean isModified() { 748 CloneableEditorSupport redirect = CloneableEditorSupportRedirector.findRedirect(this); 749 if (redirect != null) { 750 return redirect.isModified(); 751 } 752 return cesEnv().isModified(); 753 } 754 755 759 public void saveDocument() throws IOException { 760 CloneableEditorSupport redirect = CloneableEditorSupportRedirector.findRedirect(this); 761 if (redirect != null) { 762 redirect.saveDocument(); 763 return; 764 } 765 if (!cesEnv().isModified()) { 767 return; 768 } 769 770 if (lastSaveTime != -1) { 774 externallyModified = false; 775 776 cesEnv().getTime(); 780 781 if (externallyModified) { 782 return; 785 } 786 } 787 788 final StyledDocument myDoc = getDocument(); 789 if (myDoc == null) { 790 return; 791 } 792 793 class SaveAsReader implements Runnable { 795 private boolean doMarkAsUnmodified; 796 private IOException ex; 797 798 public void run() { 799 try { 800 OutputStream os = null; 801 802 long oldSaveTime = lastSaveTime; 804 805 try { 806 setLastSaveTime(-1); 807 os = new BufferedOutputStream(cesEnv().outputStream()); 808 saveFromKitToStream(myDoc, kit, os); 809 810 os.close(); os = null; 812 813 ERR.fine("Save ok, assign new time, while old was: " + oldSaveTime); setLastSaveTime(System.currentTimeMillis()); 816 817 doMarkAsUnmodified = true; 818 ERR.fine("doMarkAsUnmodified"); } catch (BadLocationException ex) { 820 Exceptions.printStackTrace(ex); 821 } finally { 822 if (lastSaveTime == -1) { ERR.fine("restoring old save time"); setLastSaveTime(oldSaveTime); 825 } 826 827 if (os != null) { os.close(); 829 } 830 } 831 832 getUndoRedo().undoableEditHappened(new UndoableEditEvent (this, new BeforeSaveEdit(lastSaveTime))); 834 835 updateLineSet(true); 837 } catch (IOException e) { 839 this.ex = e; 840 } 841 } 842 843 public void after() throws IOException { 844 if (doMarkAsUnmodified) { 845 callNotifyUnmodified(); 846 } 847 848 if (ex != null) { 849 throw ex; 850 } 851 } 852 } 853 854 SaveAsReader saveAsReader = new SaveAsReader(); 855 myDoc.render(saveAsReader); 856 saveAsReader.after(); 857 } 858 859 866 public JEditorPane [] getOpenedPanes() { 867 assert SwingUtilities.isEventDispatchThread(); CloneableEditorSupport redirect = CloneableEditorSupportRedirector.findRedirect(this); 870 if (redirect != null) { 871 return redirect.getOpenedPanes(); 872 } 873 874 LinkedList<JEditorPane > ll = new LinkedList<JEditorPane >(); 875 Enumeration en = allEditors.getComponents(); 876 877 while (en.hasMoreElements()) { 878 CloneableTopComponent ctc = (CloneableTopComponent) en.nextElement(); 879 Pane ed = (Pane ) ctc.getClientProperty(PROP_PANE); 880 881 if ((ed == null) && ctc instanceof Pane ) { 882 ed = (Pane ) ctc; 883 } 884 885 if (ed != null) { 886 JEditorPane p = ed.getEditorPane(); 889 890 if (p == null) { 891 continue; 892 } 893 894 if (getLastSelected() == ed) { 895 ll.addFirst(p); 896 } else { 897 ll.add(p); 898 } 899 } else { 900 throw new IllegalStateException ("No reference to Pane. Please file a bug against openide/text"); 901 } 902 } 903 904 return ll.isEmpty() ? null : ll.toArray(new JEditorPane [ll.size()]); 905 } 906 907 909 final Pane getLastSelected() { 910 Reference <Pane > r = lastSelected; 911 912 return (r == null) ? null : r.get(); 913 } 914 915 final void setLastSelected(Pane lastSelected) { 916 this.lastSelected = new WeakReference <Pane >(lastSelected); 917 } 918 919 923 926 public Line.Set getLineSet() { 927 CloneableEditorSupport redirect = CloneableEditorSupportRedirector.findRedirect(this); 928 if (redirect != null) { 929 return redirect.getLineSet(); 930 } 931 return updateLineSet(false); 932 } 933 934 936 final Map<Line,Reference <Line>> findWeakHashMap() { 937 synchronized (LOCK_PRINTING) { 940 if (lineSetWHM != null) { 941 return lineSetWHM; 942 } 943 944 lineSetWHM = new WeakHashMap<Line,Reference <Line>>(); 945 946 return lineSetWHM; 947 } 948 } 949 950 954 955 public void print() { 956 CloneableEditorSupport redirect = CloneableEditorSupportRedirector.findRedirect(this); 957 if (redirect != null) { 958 redirect.print(); 959 return; 960 } 961 synchronized (LOCK_PRINTING) { 963 if (printing) { 964 return; 965 } 966 967 printing = true; 968 } 969 970 try { 971 PrinterJob job = PrinterJob.getPrinterJob(); 972 Object o = NbDocument.findPageable(openDocument()); 973 974 if (o instanceof Pageable ) { 975 job.setPageable((Pageable ) o); 976 } else { 977 PageFormat pf = PrintSettings.getPageFormat(job); 978 job.setPrintable((Printable ) o, pf); 979 } 980 981 if (job.printDialog()) { 982 job.print(); 983 } 984 } catch (FileNotFoundException e) { 985 notifyProblem(e, "CTL_Bad_File"); } catch (IOException e) { 987 Exceptions.printStackTrace(e); 988 } catch (PrinterAbortException e) { notifyProblem(e, "CTL_Printer_Abort"); }catch (PrinterException e) { 991 notifyProblem(e, "EXC_Printer_Problem"); } finally { 993 synchronized (LOCK_PRINTING) { 994 printing = false; 995 } 996 } 997 } 998 999 private static void notifyProblem(Exception e, String key) { 1000 String msg = NbBundle.getMessage(CloneableEditorSupport.class, key, e.getLocalizedMessage()); 1001 Exceptions.attachLocalizedMessage(e, msg); 1002 DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Exception(e)); 1003 } 1004 1005 1009 1017 protected CloneableTopComponent createCloneableTopComponent() { 1018 prepareDocument(); 1020 1021 Pane pane = createPane(); 1022 pane.getComponent().putClientProperty(PROP_PANE, pane); 1023 1024 return pane.getComponent(); 1025 } 1026 1027 protected Pane createPane() { 1028 CloneableEditor ed = createCloneableEditor(); 1029 initializeCloneableEditor(ed); 1030 1031 return ed; 1032 } 1033 1034 1050 protected Component wrapEditorComponent(Component editorComponent) { 1051 return editorComponent; 1052 } 1053 1054 1059 protected boolean canClose() { 1060 if (cesEnv().isModified()) { 1061 1062 class SafeAWTAccess implements Runnable { 1063 boolean running; 1064 boolean finished; 1065 int ret; 1066 1067 public void run() { 1068 synchronized (this) { 1069 running = true; 1070 notifyAll(); 1071 } 1072 1073 try { 1074 ret = canCloseImpl(); 1075 } finally { 1076 synchronized (this) { 1077 finished = true; 1078 notifyAll(); 1079 } 1080 } 1081 } 1082 1083 1084 1085 public synchronized void waitForResult() throws InterruptedException { 1086 if (!running) { 1087 wait(10000); 1088 } 1089 if (!running) { 1090 throw new InterruptedException ("Waiting 10s for AWT and nothing! Exiting to prevent deadlock"); } 1092 1093 while (!finished) { 1094 wait(); 1095 } 1096 } 1097 } 1098 1099 1100 SafeAWTAccess safe = new SafeAWTAccess(); 1101 if (SwingUtilities.isEventDispatchThread()) { 1102 safe.run(); 1103 } else { 1104 SwingUtilities.invokeLater(safe); 1105 try { 1106 safe.waitForResult(); 1107 } catch (InterruptedException ex) { 1108 ERR.log(Level.INFO, null, ex); 1109 return false; 1110 } 1111 } 1112 1113 if (safe.ret == 0) { 1114 return false; 1115 } 1116 1117 if (safe.ret == 1) { 1118 try { 1119 saveDocument(); 1120 } catch (IOException e) { 1121 Exceptions.printStackTrace(e); 1122 1123 return false; 1124 } 1125 } 1126 } 1127 1128 return true; 1129 } 1130 1131 1132 private int canCloseImpl() { 1133 String msg = messageSave(); 1134 1135 ResourceBundle bundle = NbBundle.getBundle(CloneableEditorSupport.class); 1136 1137 JButton saveOption = new JButton (bundle.getString("CTL_Save")); saveOption.getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_CTL_Save")); saveOption.getAccessibleContext().setAccessibleName(bundle.getString("ACSN_CTL_Save")); 1141 JButton discardOption = new JButton (bundle.getString("CTL_Discard")); discardOption.getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_CTL_Discard")); discardOption.getAccessibleContext().setAccessibleName(bundle.getString("ACSN_CTL_Discard")); discardOption.setMnemonic(bundle.getString("CTL_Discard_Mnemonic").charAt(0)); 1146 NotifyDescriptor nd = new NotifyDescriptor( 1147 msg, bundle.getString("LBL_SaveFile_Title"), NotifyDescriptor.YES_NO_CANCEL_OPTION, 1148 NotifyDescriptor.QUESTION_MESSAGE, 1149 new Object [] { saveOption, discardOption, NotifyDescriptor.CANCEL_OPTION }, saveOption 1150 ); 1151 1152 Object ret = DialogDisplayer.getDefault().notify(nd); 1153 1154 if (NotifyDescriptor.CANCEL_OPTION.equals(ret) || NotifyDescriptor.CLOSED_OPTION.equals(ret)) { 1155 return 0; 1156 } 1157 1158 if (saveOption.equals(ret)) { 1159 return 1; 1160 } else { 1161 return -1; 1162 } 1163 } 1164 1165 1169 1172 public boolean isDocumentLoaded() { 1173 CloneableEditorSupport redirect = CloneableEditorSupportRedirector.findRedirect(this); 1174 if (redirect != null) { 1175 return redirect.isDocumentLoaded(); 1176 } 1177 return documentStatus != DOCUMENT_NO; 1178 } 1179 1180 1184 public void setMIMEType(String s) { 1185 CloneableEditorSupport redirect = CloneableEditorSupportRedirector.findRedirect(this); 1186 if (redirect != null) { 1187 redirect.setMIMEType(s); 1188 return; 1189 } 1190 mimeType = s; 1191 } 1192 1193 1199 @Deprecated 1200 public synchronized void addChangeListener(ChangeListener l) { 1201 if (listeners == null) { 1202 listeners = new HashSet<ChangeListener >(8); 1203 } 1204 1205 listeners.add(l); 1206 } 1207 1208 1213 @Deprecated 1214 public synchronized void removeChangeListener(ChangeListener l) { 1215 if (listeners != null) { 1216 listeners.remove(l); 1217 } 1218 } 1219 1220 1222 1230 public final PositionRef createPositionRef(int offset, Position.Bias bias) { 1231 return new PositionRef(getPositionManager(), offset, bias); 1232 } 1233 1234 1238 1242 protected CloneableEditor createCloneableEditor() { 1243 return new CloneableEditor(this); 1244 } 1245 1246 1252 protected void initializeCloneableEditor(CloneableEditor editor) { 1253 } 1254 1255 1263 protected UndoRedo.Manager createUndoRedoManager() { 1264 return new CESUndoRedoManager(this); 1265 } 1266 1267 1283 public InputStream getInputStream() throws IOException { 1284 CloneableEditorSupport redirect = CloneableEditorSupportRedirector.findRedirect(this); 1285 if (redirect != null) { 1286 return redirect.getInputStream(); 1287 } 1288 StyledDocument doc = getDocument(); 1294 1295 if (doc == null) { 1296 return cesEnv().inputStream(); 1297 } 1298 1299 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 1300 1301 try { 1302 saveFromKitToStream(doc, kit, baos); 1303 } catch (BadLocationException e) { 1304 ERR.log(Level.INFO, null, e); 1307 throw (IllegalStateException ) new IllegalStateException (e.getMessage()).initCause(e); 1308 } 1309 1310 return new ByteArrayInputStream(baos.toByteArray()); 1311 } 1312 1313 1325 protected void saveFromKitToStream(StyledDocument doc, EditorKit kit, OutputStream stream) 1326 throws IOException, BadLocationException { 1327 kit.write(stream, doc, 0, doc.getLength()); 1328 } 1329 1330 1342 protected void loadFromStreamToKit(StyledDocument doc, InputStream stream, EditorKit kit) 1343 throws IOException, BadLocationException { 1344 kit.read(stream, doc, 0); 1345 } 1346 1347 1351 protected Task reloadDocument() { 1352 ERR.fine("reloadDocument in " + Thread.currentThread()); 1354 if (doc != null) { 1355 NbDocument.runAtomic(doc, 1357 new Runnable () { 1358 1359 public void run() { 1360 doc.removeUndoableEditListener(getUndoRedo()); 1362 final JEditorPane [] panes = getOpenedPanes(); 1364 final int[] carets; 1365 1366 if (panes != null) { 1367 carets = new int[panes.length]; 1368 for (int i = 0; i < panes.length; i++) { 1369 carets[i] = panes[i].getCaretPosition(); 1370 } 1371 } else { 1372 carets = new int[0]; 1373 } 1374 documentStatus = DOCUMENT_RELOADING; 1375 prepareDocumentRuntimeException = null; 1376 int targetStatus = DOCUMENT_NO; 1377 1378 try { 1379 getPositionManager().documentClosed(); 1383 updateLineSet(true); 1384 fireDocumentChange(doc, true); 1385 ERR.fine("clearDocument"); 1386 clearDocument(); 1387 prepareTask = new Task(getListener()); 1389 ERR.fine("new prepare task: " + 1390 prepareTask); 1391 prepareTask.run(); 1392 ERR.fine("prepareTask finished"); 1393 documentStatus = DOCUMENT_READY; 1394 fireDocumentChange(doc, false); 1395 targetStatus = DOCUMENT_READY; 1397 } 1398 catch (RuntimeException t) { 1399 prepareDocumentRuntimeException = t; 1400 prepareTask = null; 1401 Exceptions.printStackTrace(t); 1402 throw t; 1403 } 1404 catch (Error t) { 1405 prepareDocumentRuntimeException = t; 1406 prepareTask = null; 1407 Exceptions.printStackTrace(t); 1408 throw t; 1409 } 1410 finally { 1411 synchronized (getLock()) { 1412 documentStatus = targetStatus; 1413 getLock().notifyAll(); 1414 } 1415 } 1416 ERR.fine("post-reload task posting to AWT"); 1417 Runnable run = new Runnable () { 1418 1419 public void run() { 1420 if (doc == null) { 1421 return; 1422 } 1423 if (panes != null) { 1424 for (int i = 0; i < 1425 panes.length; i++) { 1426 int textLength = panes[i].getDocument().getLength(); 1429 1430 if (carets[i] > 1431 textLength) { 1432 carets[i] = textLength; 1433 } 1434 panes[i].setCaretPosition(carets[i]); 1435 } 1436 } 1437 ERR.fine("task-discardAllEdits"); 1439 getUndoRedo().discardAllEdits(); 1440 ERR.fine("task-undoableEditHappened"); 1442 getUndoRedo().undoableEditHappened(new UndoableEditEvent (CloneableEditorSupport.this, 1443 new BeforeSaveEdit(lastSaveTime))); 1444 ERR.fine("task-check already modified"); 1445 if (isAlreadyModified()) { 1447 ERR.fine("task-callNotifyUnmodified"); 1448 callNotifyUnmodified(); 1449 } 1450 updateLineSet(true); 1451 ERR.fine("task-addUndoableEditListener"); 1452 doc.addUndoableEditListener(getUndoRedo()); 1454 } 1455 }; 1456 1457 if (doc != null) { 1458 ERR.fine("Posting the AWT runnable: " + 1459 run); 1460 SwingUtilities.invokeLater(run); 1461 ERR.fine("Posted in " + 1462 Thread.currentThread()); 1463 } 1464 } 1465 }); 1466 1467 return prepareTask; 1468 } 1469 1470 return prepareDocument(); 1471 } 1472 1473 1502 public static EditorKit getEditorKit(String mimePath) { 1503 Lookup lookup = MimeLookup.getLookup(MimePath.parse(mimePath)); 1504 EditorKit kit = lookup.lookup(EditorKit.class); 1505 1506 if (kit == null) { 1507 lookup = MimeLookup.getLookup(MimePath.parse("text/plain")); 1509 kit = lookup.lookup(EditorKit.class); 1510 } 1511 1512 return kit != null ? (EditorKit) kit.clone() : new PlainEditorKit(); 1514 } 1515 1516 1519 protected EditorKit createEditorKit() { 1520 if (kit != null) { 1521 return kit; 1522 } 1523 1524 if (mimeType != null) { 1525 kit = getEditorKit(mimeType); 1526 } else { 1527 String defaultMIMEType = cesEnv().getMimeType(); 1528 kit = getEditorKit(defaultMIMEType); 1529 } 1530 1531 return kit; 1532 } 1533 1534 1540 protected StyledDocument createStyledDocument(EditorKit kit) { 1541 StyledDocument sd = createNetBeansDocument(kit.createDefaultDocument()); 1542 sd.putProperty("mimeType", (mimeType != null) ? mimeType : cesEnv().getMimeType()); 1544 return sd; 1545 } 1546 1547 1553 protected void notifyUnmodified() { 1554 env.unmarkModified(); 1555 updateTitles(); 1556 } 1557 1558 1561 final boolean callNotifyModified() { 1562 if (!isAlreadyModified() && !revertingUndoOrReloading) { 1577 setAlreadyModified(true); 1578 1579 if (!notifyModified()) { 1580 setAlreadyModified(false); 1581 revertingUndoOrReloading = true; 1582 revertPreviousOrUpcomingUndo(); 1583 revertingUndoOrReloading = false; 1584 1585 return false; 1586 } 1587 } 1588 1589 return true; 1590 } 1591 1592 final void callNotifyUnmodified() { 1593 setAlreadyModified(false); 1594 notifyUnmodified(); 1595 } 1596 1597 1606 protected boolean notifyModified() { 1607 boolean locked = true; 1608 1609 try { 1610 env.markModified(); 1611 } catch (final UserQuestionException ex) { 1612 synchronized (this) { 1613 if (!this.inUserQuestionExceptionHandler) { 1614 this.inUserQuestionExceptionHandler = true; 1615 RequestProcessor.getDefault().post(new Runnable () { 1616 1617 public void run() { 1618 NotifyDescriptor nd = new NotifyDescriptor.Confirmation(ex.getLocalizedMessage(), 1619 NotifyDescriptor.YES_NO_OPTION); 1620 Object res = DialogDisplayer.getDefault().notify(nd); 1621 1622 if (NotifyDescriptor.OK_OPTION.equals(res)) { 1623 try { 1624 ex.confirmed(); 1625 } 1626 catch (IOException ex1) { 1627 Exceptions.printStackTrace(ex1); 1628 } 1629 } 1630 synchronized (CloneableEditorSupport.this) { 1631 CloneableEditorSupport.this.inUserQuestionExceptionHandler = false; 1632 } 1633 } 1634 }); 1635 } 1636 } 1637 1638 locked = false; 1639 } catch (IOException e) { 1641 String message = null; 1642 1643 if (e.getMessage() != e.getLocalizedMessage()) { 1644 message = e.getLocalizedMessage(); 1645 } else { 1646 message = Exceptions.findLocalizedMessage(e); 1647 } 1648 1649 if (message != null) { 1650 StatusDisplayer.getDefault().setStatusText(message); 1651 } 1652 1653 locked = false; 1654 } 1655 1656 if (!locked) { 1657 return false; 1658 } 1659 1660 lastReusable.clear(); 1662 updateTitles(); 1663 1664 return true; 1665 } 1666 1667 1670 private void revertPreviousOrUpcomingUndo() { 1671 UndoRedo.Manager ur = getUndoRedo(); 1672 Listener l = getListener(); 1673 1674 if (Boolean.TRUE.equals(getDocument().getProperty("supportsModificationListener"))) { 1676 SearchBeforeModificationEdit edit = new SearchBeforeModificationEdit(); 1678 1679 try { 1680 for (;;) { 1681 edit.delegate = null; 1682 ur.undoableEditHappened(new UndoableEditEvent (getDocument(), edit)); 1683 1684 if (edit.delegate == null) break; 1686 if (edit.delegate instanceof BeforeModificationEdit) { 1687 if (edit.delegate != null) { 1688 ur.undo(); 1690 } 1691 1692 break; 1694 } 1695 1696 if (edit.delegate instanceof BeforeSaveEdit) { 1697 break; 1698 } 1699 1700 ur.undo(); 1702 } 1703 } catch (CannotUndoException ex) { 1704 } 1706 } else { 1707 l.setUndoTask(new Runnable () { 1709 public void run() { 1710 undoAll(); 1711 } 1712 } 1713 ); 1714 ur.addChangeListener(l); 1715 } 1716 } 1717 1718 1720 final void undoAll() { 1721 StyledDocument sd = doc; 1722 1723 if (sd == null) { 1724 return; 1726 } 1727 1728 UndoRedo ur = getUndoRedo(); 1729 addRemoveDocListener(sd, false); 1730 1731 try { 1732 if (ur.canUndo()) { 1733 Toolkit.getDefaultToolkit().beep(); 1734 ur.undo(); 1735 } 1736 } catch (CannotUndoException cne) { 1737 ERR.log(Level.INFO, null, cne); 1738 } finally { 1739 addRemoveDocListener(sd, true); 1740 } 1741 } 1742 1743 1747 protected void notifyClosed() { 1748 closeDocument(); 1749 } 1750 1751 1753 1754 boolean isEnvReadOnly() { 1755 return false; 1756 } 1757 1758 1760 final StyledDocument getDocumentHack() { 1761 return doc; 1762 } 1763 1764 1767 final org.openide.util.Lookup getLookup() { 1768 return lookup; 1769 } 1770 1771 1773 1777 Line.Set updateLineSet(boolean clear) { 1778 synchronized (getLock()) { 1779 if ((lineSet != null) && !clear) { 1780 return lineSet; 1781 } 1782 1783 Line.Set oldSet = lineSet; 1784 1785 if ((doc == null) || (documentStatus == DOCUMENT_RELOADING)) { 1786 lineSet = new EditorSupportLineSet.Closed(CloneableEditorSupport.this); 1787 } else { 1788 lineSet = new EditorSupportLineSet(CloneableEditorSupport.this, doc); 1789 } 1790 1791 return lineSet; 1792 } 1793 } 1794 1795 1799 private void loadDocument(EditorKit kit, StyledDocument doc) 1800 throws IOException { 1801 Throwable aProblem = null; 1802 1803 try { 1804 InputStream is = new BufferedInputStream(cesEnv().inputStream()); 1805 1806 try { 1807 loadFromStreamToKit(doc, is, kit); 1809 } finally { 1810 is.close(); 1811 } 1812 } catch (UserQuestionException ex) { 1813 throw ex; 1814 } catch (IOException ex) { 1815 aProblem = ex; 1816 throw ex; 1817 } catch (Exception e) { aProblem = e; 1819 } finally { 1820 if (aProblem != null) { 1821 final Throwable tmp = aProblem; 1822 SwingUtilities.invokeLater(new Runnable () { 1823 1824 public void run() { 1825 Exceptions.attachLocalizedMessage(tmp, 1826 NbBundle.getMessage(CloneableEditorSupport.class, 1827 "EXC_LoadDocument", 1828 messageName())); 1829 Exceptions.printStackTrace(tmp); 1830 } 1831 }); 1832 } 1833 } 1834 } 1835 1836 1842 protected boolean close(boolean ask) { 1843 CloneableEditorSupport redirect = CloneableEditorSupportRedirector.findRedirect(this); 1844 if (redirect != null) { 1845 return redirect.close(ask); 1846 } 1847 1848 if (!super.close(ask)) { 1849 return false; 1851 } 1852 1853 notifyClosed(); 1854 1855 return true; 1856 } 1857 1858 1860 private void closeDocument() { 1861 synchronized (getLock()) { 1862 while (true) { 1863 switch (documentStatus) { 1864 case DOCUMENT_NO: 1865 return; 1866 1867 case DOCUMENT_LOADING: 1868 case DOCUMENT_RELOADING: 1869 default: 1873 doCloseDocument(); 1874 1875 return; 1876 } 1877 } 1878 } 1879 } 1880 1881 1883 private void doCloseDocument() { 1884 prepareTask = null; 1885 1886 cesEnv().removePropertyChangeListener(getListener()); 1888 callNotifyUnmodified(); 1889 1890 if (doc != null) { 1891 doc.removeUndoableEditListener(getUndoRedo()); 1892 addRemoveDocListener(doc, false); 1893 } 1894 1895 if (positionManager != null) { 1896 positionManager.documentClosed(); 1897 1898 documentStatus = DOCUMENT_NO; 1899 fireDocumentChange(doc, true); 1900 } 1901 1902 documentStatus = DOCUMENT_NO; 1903 doc = null; 1904 kit = null; 1905 1906 getUndoRedo().discardAllEdits(); 1907 updateLineSet(true); 1908 } 1909 1910 1913 private void checkReload(boolean doReload) { 1914 StyledDocument d; 1915 1916 synchronized (getLock()) { 1917 if (documentStatus != DOCUMENT_READY) { 1919 return; 1920 } 1921 1922 d = doc; } 1924 1925 if (!doReload && !reloadDialogOpened) { 1926 String msg = NbBundle.getMessage( 1927 CloneableEditorSupport.class, "FMT_External_change", d.getProperty(javax.swing.text.Document.TitleProperty) 1929 ); 1930 1931 NotifyDescriptor nd = new NotifyDescriptor.Confirmation(msg, NotifyDescriptor.YES_NO_OPTION); 1932 1933 reloadDialogOpened = true; 1934 1935 try { 1936 Object ret = DialogDisplayer.getDefault().notify(nd); 1937 1938 if (NotifyDescriptor.YES_OPTION.equals(ret)) { 1939 doReload = true; 1940 } 1941 } finally { 1942 reloadDialogOpened = false; 1943 } 1944 } 1945 1946 synchronized (getLock()) { 1947 if (documentStatus != DOCUMENT_READY) { 1949 return; 1950 } 1951 1952 if (doReload) { 1953 reloadDocument(); 1957 1958 1967 } 1968 } 1969 } 1970 1971 1975 private static StyledDocument createNetBeansDocument(Document d) { 1976 if (d instanceof StyledDocument) { 1977 return (StyledDocument) d; 1978 } else { 1979 return new FilterDocument(d); 1981 } 1982 } 1983 1984 private final void fireDocumentChange(StyledDocument document, boolean closing) { 1985 fireStateChangeEvent(document, closing); 1986 firePropertyChange(EditorCookie.Observable.PROP_DOCUMENT, 1987 closing ? document : null, 1988 closing ? null : document); 1989 } 1990 1991 1992 private final void fireStateChangeEvent(StyledDocument document, boolean closing) { 1993 if (listeners != null) { 1994 EnhancedChangeEvent event = new EnhancedChangeEvent(this, document, closing); 1995 ChangeListener [] ls; 1996 1997 synchronized (this) { 1998 ls = listeners.toArray(new ChangeListener [listeners.size()]); 1999 } 2000 2001 for (ChangeListener l : ls) { 2002 l.stateChanged(event); 2003 } 2004 } 2005 } 2006 2007 2009 protected void updateTitles() { 2010 Enumeration en = allEditors.getComponents(); 2011 2012 while (en.hasMoreElements()) { 2013 CloneableTopComponent o = (CloneableTopComponent) en.nextElement(); 2014 Pane e = (Pane ) o.getClientProperty(PROP_PANE); 2015 2016 if ((e == null) && o instanceof Pane ) { 2017 e = (Pane ) o; 2018 } 2019 2020 if (e != null) { 2021 e.updateName(); 2022 } else { 2023 throw new IllegalStateException ("No reference to Pane. Please file a bug against openide/text"); 2024 } 2025 } 2026 } 2027 2028 private static Reference <CloneableTopComponent> lastReusable = new WeakReference (null); 2029 2030 private static void replaceTc(TopComponent orig, TopComponent open) { 2032 orig.close(); 2033 open.open(); 2034 } 2035 2036 2039 2040 private Pane openPane(boolean reuse) { 2041 Pane ce = null; 2042 boolean displayMsgOpened = false; 2043 2044 synchronized (getLock()) { 2045 ce = getAnyEditor(); 2046 2047 if (ce == null) { 2048 String msg = messageOpening(); 2050 2051 if (msg != null) { 2052 StatusDisplayer.getDefault().setStatusText(msg); 2053 } 2054 2055 prepareDocument(); 2057 ce = createPane(); 2058 ce.getComponent().putClientProperty(PROP_PANE, ce); 2059 ce.getComponent().setReference(allEditors); 2060 2061 displayMsgOpened = true; 2063 } 2064 } 2065 2066 CloneableTopComponent ctc = ce.getComponent(); 2068 if (reuse && displayMsgOpened) { 2069 CloneableTopComponent last = lastReusable.get(); 2070 if (last != null) { 2071 replaceTc(last, ctc); 2072 } else { 2073 ctc.open(); 2074 } 2075 lastReusable = new WeakReference (ctc); 2076 } else { 2077 ctc.open(); 2078 } 2079 2080 if (displayMsgOpened) { 2081 String msg = messageOpened(); 2082 2083 if (msg == null) { 2084 msg = ""; } 2086 2087 StatusDisplayer.getDefault().setStatusText(msg); 2088 } 2089 2090 return ce; 2091 } 2092 2093 2096 private Pane getAnyEditor() { 2097 CloneableTopComponent ctc; 2098 ctc = allEditors.getArbitraryComponent(); 2099 2100 if (ctc == null) { 2101 return null; 2102 } 2103 2104 Pane e = (Pane ) ctc.getClientProperty(PROP_PANE); 2105 2106 if (e != null) { 2107 return e; 2108 } else { 2109 if (ctc instanceof Pane ) { 2110 return (Pane ) ctc; 2111 } 2112 2113 Enumeration en = allEditors.getComponents(); 2114 2115 while (en.hasMoreElements()) { 2116 ctc = (CloneableTopComponent) en.nextElement(); 2117 e = (Pane ) ctc.getClientProperty(PROP_PANE); 2118 2119 if (e != null) { 2120 return e; 2121 } else { 2122 if (ctc instanceof Pane ) { 2123 return (Pane ) ctc; 2124 } 2125 2126 throw new IllegalStateException ("No reference to Pane. Please file a bug against openide/text"); 2127 } 2128 } 2129 2130 return null; 2131 } 2132 } 2133 2134 final Pane openReuse(final PositionRef pos, final int column, int mode) { 2135 if (mode == Line.SHOW_REUSE_NEW) lastReusable.clear(); 2136 return openAtImpl(pos, column, true); 2137 } 2138 2139 2146 protected final Pane openAt(final PositionRef pos, final int column) { 2147 return openAtImpl(pos, column, false); 2148 } 2149 2157 private final Pane openAtImpl(final PositionRef pos, final int column, boolean reuse) { 2158 CloneableEditorSupport redirect = CloneableEditorSupportRedirector.findRedirect(this); 2159 if (redirect != null) { 2160 return redirect.openAtImpl(pos, column, reuse); 2161 } 2162 final Pane e = openPane(reuse); 2163 final Task t = prepareDocument(); 2164 e.ensureVisible(); 2165 class Selector implements TaskListener, Runnable { 2166 public void taskFinished(org.openide.util.Task t2) { 2167 javax.swing.SwingUtilities.invokeLater(this); 2168 t2.removeTaskListener(this); 2169 } 2170 2171 public void run() { 2172 JEditorPane ePane = e.getEditorPane(); 2174 2175 if (ePane == null) { 2176 return; 2177 } 2178 2179 StyledDocument doc = getDocument(); 2180 2181 if (doc == null) { 2182 return; } 2184 2185 Caret caret = ePane.getCaret(); 2186 2187 if (caret == null) { 2188 return; 2189 } 2190 2191 int offset; 2192 2193 if (column >= 0) { 2194 javax.swing.text.Element el = NbDocument.findLineRootElement(doc); 2195 el = el.getElement(el.getElementIndex(pos.getOffset())); 2196 offset = el.getStartOffset() + column; 2197 2198 if (offset > el.getEndOffset()) { 2199 offset = el.getEndOffset(); 2200 } 2201 } else { 2202 offset = pos.getOffset(); 2203 } 2204 2205 caret.setDot(offset); 2206 } 2207 } 2208 t.addTaskListener(new Selector()); 2209 2210 return e; 2211 } 2212 2213 2215 final Object getLock() { 2216 return allEditors; 2217 } 2218 2219 2223 private Listener getListener() { 2224 if (listener == null) { 2227 listener = new Listener(); 2228 } 2229 2230 return listener; 2231 } 2232 2233 void howToReproduceDeadlock40766(boolean beforeLock) { 2235 } 2236 2237 2240 final void setLastSaveTime(long lst) { 2241 ERR.fine("Setting new lastSaveTime to " + lst); 2242 this.lastSaveTime = lst; 2243 } 2244 2245 final boolean isAlreadyModified() { 2246 return alreadyModified; 2247 } 2248 2249 final void setAlreadyModified(boolean alreadyModified) { 2250 ERR.log(Level.FINE, null, new Exception ("Setting to modified: " + alreadyModified)); 2251 2252 this.alreadyModified = alreadyModified; 2253 } 2254 2255 2259 2262 public static interface Env extends CloneableOpenSupport.Env { 2263 2264 public static final String PROP_TIME = "time"; 2266 2270 public InputStream inputStream() throws IOException; 2271 2272 2276 public OutputStream outputStream() throws IOException; 2277 2278 2283 public Date getTime(); 2284 2285 2288 public String getMimeType(); 2289 } 2290 2291 2293 public interface Pane { 2294 2297 public JEditorPane getEditorPane(); 2298 2299 2302 public CloneableTopComponent getComponent(); 2303 2304 public void updateName(); 2305 2306 2309 public void ensureVisible(); 2310 } 2311 2312 2314 private static final class PlainEditorKit extends DefaultEditorKit implements ViewFactory { 2315 static final long serialVersionUID = -5788777967029507963L; 2316 2317 PlainEditorKit() { 2318 } 2319 2320 2322 public Object clone() { 2323 return new PlainEditorKit(); 2324 } 2325 2326 2328 public ViewFactory getViewFactory() { 2329 return this; 2330 } 2331 2332 2334 public View create(Element elem) { 2335 return new WrappedPlainView(elem); 2336 } 2337 2338 2339 public void install(JEditorPane pane) { 2340 super.install(pane); 2341 pane.setFont(new Font ("Monospaced", Font.PLAIN, pane.getFont().getSize() + 1)); } 2343 } 2344 2345 2348 private final class Listener extends Object implements ChangeListener , DocumentListener , PropertyChangeListener , 2349 Runnable , java.beans.VetoableChangeListener { 2350 2351 private boolean revertModifiedFlag; 2352 2353 2354 private IOException loadExc; 2355 2356 2358 private Runnable undoTask; 2359 2360 Listener() { 2361 } 2362 2363 2366 public IOException checkLoadException() { 2367 IOException ret = loadExc; 2368 2369 return ret; 2371 } 2372 2373 2374 public void setUndoTask(Runnable undoTask) { 2375 this.undoTask = undoTask; 2376 } 2377 2378 2381 public void stateChanged(ChangeEvent evt) { 2382 getUndoRedo().removeChangeListener(this); 2383 undoTask.run(); 2384 2385 undoTask = null; 2387 } 2388 2389 2392 public void changedUpdate(DocumentEvent ev) { 2393 } 2395 2396 public void vetoableChange(PropertyChangeEvent evt) 2397 throws java.beans.PropertyVetoException { 2398 if ("modified".equals(evt.getPropertyName())) { 2400 if (Boolean.TRUE.equals(evt.getNewValue())) { 2401 boolean wasModified = isAlreadyModified(); 2402 2403 if (!callNotifyModified()) { 2404 throw new java.beans.PropertyVetoException ("Not allowed", evt); } 2406 2407 revertModifiedFlag = !wasModified; 2408 } else { 2409 if (revertModifiedFlag) { 2410 callNotifyUnmodified(); 2411 } 2412 } 2413 } 2414 } 2415 2416 2419 public void insertUpdate(DocumentEvent ev) { 2420 callNotifyModified(); 2421 revertModifiedFlag = false; 2422 } 2423 2424 2427 public void removeUpdate(DocumentEvent ev) { 2428 callNotifyModified(); 2429 revertModifiedFlag = false; 2430 } 2431 2432 2434 public void propertyChange(PropertyChangeEvent ev) { 2435 if (Env.PROP_TIME.equals(ev.getPropertyName())) { 2436 final Date time = (Date) ev.getNewValue(); 2438 2439 ERR.fine("PROP_TIME new value: " + time); 2440 ERR.fine(" lastSaveTime: " + lastSaveTime); 2441 2442 boolean reload = (lastSaveTime != -1) && ((time == null) || (time.getTime() > lastSaveTime)); 2443 ERR.fine(" reload: " + reload); 2444 2445 if (reload) { 2446 externallyModified = true; 2448 2449 SwingUtilities.invokeLater( 2453 new Runnable () { 2454 boolean inWriteAccess; 2455 2456 public void run() { 2457 if (!inWriteAccess) { 2458 inWriteAccess = true; 2459 2460 StyledDocument sd = doc; 2461 2462 if (sd == null) { 2463 return; 2464 } 2465 2466 revertingUndoOrReloading = true; 2468 NbDocument.runAtomic(sd, this); 2469 revertingUndoOrReloading = false; 2471 return; 2472 } 2473 2474 checkReload((time == null) || !isModified()); 2475 } 2476 } 2477 ); 2478 } 2479 } 2480 2481 if (Env.PROP_MODIFIED.equals(ev.getPropertyName())) { 2482 CloneableEditorSupport.this.firePropertyChange( 2483 EditorCookie.Observable.PROP_MODIFIED, ev.getOldValue(), ev.getNewValue() 2484 ); 2485 } 2486 } 2487 2488 2490 public void run() { 2491 2493 2497 addRemoveDocListener(doc, false); 2498 2499 try { 2500 loadExc = null; 2501 LOCAL_LOAD_TASK.set(true); 2502 loadDocument(kit, doc); 2503 } catch (IOException e) { 2504 loadExc = e; 2505 throw new DelegateIOExc(e); 2506 } finally { 2507 LOCAL_LOAD_TASK.set(null); 2508 } 2509 2510 getPositionManager().documentOpened(doc); 2512 2513 updateLineSet(true); 2515 2516 setLastSaveTime(System.currentTimeMillis()); 2517 2518 getUndoRedo().undoableEditHappened(new UndoableEditEvent (this, new BeforeSaveEdit(lastSaveTime))); 2520 2521 addRemoveDocListener(doc, true); 2523 } 2524 2525 } 2527 2528 2529 private class FilterUndoableEdit implements UndoableEdit { 2530 protected UndoableEdit delegate; 2531 2532 FilterUndoableEdit() { 2533 } 2534 2535 public void undo() throws CannotUndoException { 2536 if (delegate != null) { 2537 delegate.undo(); 2538 } 2539 } 2540 2541 public boolean canUndo() { 2542 if (delegate != null) { 2543 return delegate.canUndo(); 2544 } else { 2545 return false; 2546 } 2547 } 2548 2549 public void redo() throws CannotRedoException { 2550 if (delegate != null) { 2551 delegate.redo(); 2552 } 2553 } 2554 2555 public boolean canRedo() { 2556 if (delegate != null) { 2557 return delegate.canRedo(); 2558 } else { 2559 return false; 2560 } 2561 } 2562 2563 public void die() { 2564 if (delegate != null) { 2565 delegate.die(); 2566 } 2567 } 2568 2569 public boolean addEdit(UndoableEdit anEdit) { 2570 if (delegate != null) { 2571 return delegate.addEdit(anEdit); 2572 } else { 2573 return false; 2574 } 2575 } 2576 2577 public boolean replaceEdit(UndoableEdit anEdit) { 2578 if (delegate != null) { 2579 return delegate.replaceEdit(anEdit); 2580 } else { 2581 return false; 2582 } 2583 } 2584 2585 public boolean isSignificant() { 2586 if (delegate != null) { 2587 return delegate.isSignificant(); 2588 } else { 2589 return true; 2590 } 2591 } 2592 2593 public String getPresentationName() { 2594 if (delegate != null) { 2595 return delegate.getPresentationName(); 2596 } else { 2597 return ""; } 2599 } 2600 2601 public String getUndoPresentationName() { 2602 if (delegate != null) { 2603 return delegate.getUndoPresentationName(); 2604 } else { 2605 return ""; } 2607 } 2608 2609 public String getRedoPresentationName() { 2610 if (delegate != null) { 2611 return delegate.getRedoPresentationName(); 2612 } else { 2613 return ""; } 2615 } 2616 } 2617 2618 2625 private class BeforeSaveEdit extends FilterUndoableEdit { 2626 private long saveTime; 2627 2628 BeforeSaveEdit(long saveTime) { 2629 this.saveTime = saveTime; 2630 } 2631 2632 public boolean replaceEdit(UndoableEdit anEdit) { 2633 if (delegate == null) { 2634 delegate = anEdit; 2635 2636 return true; } 2638 2639 return false; 2640 } 2641 2642 public boolean addEdit(UndoableEdit anEdit) { 2643 if (!(anEdit instanceof BeforeModificationEdit) && !(anEdit instanceof SearchBeforeModificationEdit)) { 2644 2647 getUndoRedo().addEdit(new BeforeModificationEdit(saveTime, anEdit)); 2648 2649 return true; 2650 } 2651 2652 return false; 2653 } 2654 2655 public void redo() { 2656 super.redo(); 2657 2658 if (saveTime == lastSaveTime) { 2659 justRevertedToNotModified = true; 2660 } 2661 } 2662 2663 public boolean isSignificant() { 2664 return (delegate != null); 2665 } 2666 } 2667 2668 2671 private class BeforeModificationEdit extends FilterUndoableEdit { 2672 private long saveTime; 2673 2674 BeforeModificationEdit(long saveTime, UndoableEdit delegate) { 2675 this.saveTime = saveTime; 2676 this.delegate = delegate; 2677 ERR.log(Level.FINE, null, new Exception ("new BeforeModificationEdit(" + saveTime +")")); } 2679 2680 public boolean addEdit(UndoableEdit anEdit) { 2681 if ((delegate == null) && !(anEdit instanceof SearchBeforeModificationEdit)) { 2682 delegate = anEdit; 2683 2684 return true; 2685 } 2686 2687 return delegate.addEdit(anEdit); 2688 } 2689 2690 public void undo() { 2691 super.undo(); 2692 2693 boolean res = saveTime == lastSaveTime; 2694 ERR.fine("Comparing saveTime and lastSaveTime: " + saveTime + "==" + lastSaveTime + " is " + res); if (res) { 2696 justRevertedToNotModified = true; 2697 } 2698 } 2699 } 2700 2701 2705 private class SearchBeforeModificationEdit extends FilterUndoableEdit { 2706 SearchBeforeModificationEdit() { 2707 } 2708 2709 public boolean replaceEdit(UndoableEdit anEdit) { 2710 if (delegate == null) { 2711 delegate = anEdit; 2712 2713 return true; } 2715 2716 return false; 2717 } 2718 } 2719 2720 2723 private final static class CESUndoRedoManager extends UndoRedo.Manager { 2724 private CloneableEditorSupport support; 2725 2726 public CESUndoRedoManager(CloneableEditorSupport c) { 2727 this.support = c; 2728 super.setLimit(1000); 2729 } 2730 2731 public void redo() throws javax.swing.undo.CannotRedoException { 2732 final StyledDocument myDoc = support.getDocument(); 2733 2734 if (myDoc == null) { 2735 throw new javax.swing.undo.CannotRedoException (); } 2737 2738 support.justRevertedToNotModified = false; 2739 new RenderUndo(0, myDoc); 2740 2741 if (support.justRevertedToNotModified && support.isAlreadyModified()) { 2742 support.callNotifyUnmodified(); 2743 } 2744 } 2745 2746 public void undo() throws javax.swing.undo.CannotUndoException { 2747 final StyledDocument myDoc = support.getDocument(); 2748 2749 if (myDoc == null) { 2750 throw new javax.swing.undo.CannotUndoException (); } 2752 2753 support.justRevertedToNotModified = false; 2754 new RenderUndo(1, myDoc); 2755 2756 if (support.justRevertedToNotModified && support.isAlreadyModified()) { 2757 support.callNotifyUnmodified(); 2758 } 2759 } 2760 2761 public boolean canRedo() { 2762 final StyledDocument myDoc = support.getDocument(); 2763 2764 return new RenderUndo(2, myDoc).booleanResult; 2765 } 2766 2767 public boolean canUndo() { 2768 final StyledDocument myDoc = support.getDocument(); 2769 2770 return new RenderUndo(3, myDoc).booleanResult; 2771 } 2772 2773 public int getLimit() { 2774 final StyledDocument myDoc = support.getDocument(); 2775 2776 return new RenderUndo(4, myDoc).intResult; 2777 } 2778 2779 public void discardAllEdits() { 2780 final StyledDocument myDoc = support.getDocument(); 2781 new RenderUndo(5, myDoc); 2782 } 2783 2784 public void setLimit(int l) { 2785 final StyledDocument myDoc = support.getDocument(); 2786 new RenderUndo(6, myDoc, l); 2787 } 2788 2789 public boolean canUndoOrRedo() { 2790 final StyledDocument myDoc = support.getDocument(); 2791 2792 return new RenderUndo(7, myDoc).booleanResult; 2793 } 2794 2795 public java.lang.String getUndoOrRedoPresentationName() { 2796 final StyledDocument myDoc = support.getDocument(); 2797 2798 return new RenderUndo(8, myDoc).stringResult; 2799 } 2800 2801 public java.lang.String getRedoPresentationName() { 2802 final StyledDocument myDoc = support.getDocument(); 2803 2804 return new RenderUndo(9, myDoc).stringResult; 2805 } 2806 2807 public java.lang.String getUndoPresentationName() { 2808 final StyledDocument myDoc = support.getDocument(); 2809 2810 return new RenderUndo(10, myDoc).stringResult; 2811 } 2812 2813 public void undoOrRedo() throws javax.swing.undo.CannotUndoException , javax.swing.undo.CannotRedoException { 2814 final StyledDocument myDoc = support.getDocument(); 2815 2816 if (myDoc == null) { 2817 throw new javax.swing.undo.CannotUndoException (); } 2819 2820 support.justRevertedToNotModified = false; 2821 new RenderUndo(11, myDoc); 2822 2823 if (support.justRevertedToNotModified && support.isAlreadyModified()) { 2824 support.callNotifyUnmodified(); 2825 } 2826 } 2827 2828 private final class RenderUndo implements Runnable { 2829 private final int type; 2830 public boolean booleanResult; 2831 public int intResult; 2832 public String stringResult; 2833 2834 public RenderUndo(int type, StyledDocument doc) { 2835 this(type, doc, 0); 2836 } 2837 2838 public RenderUndo(int type, StyledDocument doc, int intValue) { 2839 this.type = type; 2840 this.intResult = intValue; 2841 2842 if (doc instanceof NbDocument.WriteLockable) { 2843 ((NbDocument.WriteLockable) doc).runAtomic(this); 2844 } else { 2845 run(); 2852 } 2853 } 2854 2855 public void run() { 2856 switch (type) { 2857 case 0: 2858 CESUndoRedoManager.super.redo(); 2859 2860 break; 2861 2862 case 1: 2863 CESUndoRedoManager.super.undo(); 2864 2865 break; 2866 2867 case 2: 2868 booleanResult = CESUndoRedoManager.super.canRedo(); 2869 2870 break; 2871 2872 case 3: 2873 booleanResult = CESUndoRedoManager.super.canUndo(); 2874 2875 break; 2876 2877 case 4: 2878 intResult = CESUndoRedoManager.super.getLimit(); 2879 2880 break; 2881 2882 case 5: 2883 CESUndoRedoManager.super.discardAllEdits(); 2884 2885 break; 2886 2887 case 6: 2888 CESUndoRedoManager.super.setLimit(intResult); 2889 2890 break; 2891 2892 case 7: 2893 CESUndoRedoManager.super.canUndoOrRedo(); 2894 2895 break; 2896 2897 case 8: 2898 stringResult = CESUndoRedoManager.super.getUndoOrRedoPresentationName(); 2899 2900 break; 2901 2902 case 9: 2903 stringResult = CESUndoRedoManager.super.getRedoPresentationName(); 2904 2905 break; 2906 2907 case 10: 2908 stringResult = CESUndoRedoManager.super.getUndoPresentationName(); 2909 2910 break; 2911 2912 case 11: 2913 CESUndoRedoManager.super.undoOrRedo(); 2914 2915 break; 2916 2917 default: 2918 throw new IllegalArgumentException ("Unknown type: " + type); 2919 } 2920 } 2921 } 2922 } 2923 2924 2926 private static final class DelegateIOExc extends IllegalStateException { 2927 public DelegateIOExc(IOException ex) { 2928 super(ex.getMessage()); 2929 initCause(ex); 2930 } 2931 } 2932 2933} 2934 | Popular Tags |