1 21 22 package org.armedbear.j; 23 24 import java.awt.Cursor ; 25 import java.io.BufferedInputStream ; 26 import java.io.BufferedOutputStream ; 27 import java.io.BufferedReader ; 28 import java.io.IOException ; 29 import java.io.InputStream ; 30 import java.io.InputStreamReader ; 31 import java.io.OutputStream ; 32 import java.lang.ref.SoftReference ; 33 import java.util.ArrayList ; 34 import java.util.Iterator ; 35 import java.util.List ; 36 import java.util.zip.GZIPInputStream ; 37 import java.util.zip.GZIPOutputStream ; 38 import java.util.zip.ZipEntry ; 39 import javax.swing.Icon ; 40 import javax.swing.SwingUtilities ; 41 import javax.swing.undo.CannotRedoException ; 42 import javax.swing.undo.CannotUndoException ; 43 import javax.swing.undo.CompoundEdit ; 44 import javax.swing.undo.UndoableEdit ; 45 46 public class Buffer extends SystemBuffer 47 { 48 private static int untitledCount; 49 50 protected boolean isUntitled; 51 52 protected Formatter formatter; 53 54 protected String title; 55 56 private boolean needsParsing; 57 58 boolean needsRenumbering; 59 60 public final boolean needsRenumbering() 61 { 62 return needsRenumbering; 63 } 64 65 public final void needsRenumbering(boolean b) 66 { 67 needsRenumbering = b; 68 } 69 70 private int visibleLineCount; 71 72 protected boolean supportsUndo = true; 73 74 public final boolean supportsUndo() 75 { 76 return supportsUndo; 77 } 78 79 private int modCount; 80 private int saveModCount; 82 protected boolean autosaveEnabled; 84 private File autosaveFile; 85 private int autosaveModCount; 87 private File cache; 88 private String listing; 89 public final String getListing() 90 { 91 return listing; 92 } 93 public final void setListing(String s) 94 { 95 this.listing = s; 96 } 97 98 private View lastView; 99 100 private boolean backedUp = false; 102 private int fileType = FILETYPE_UNKNOWN; 103 104 private Compression compression; 105 106 public final Compression getCompression() 107 { 108 return compression; 109 } 110 111 public final void setCompression(Compression compression) 112 { 113 this.compression = compression; 114 } 115 116 protected PropertyList properties = new PropertyList(); 117 118 private BackgroundProcess backgroundProcess; 119 120 private Mutex mutex = new Mutex(); 121 private final ReadWriteLock rwlock = new ReadWriteLock(); 122 123 private boolean isNewFile; 124 125 protected Buffer parentBuffer; 126 127 public final Buffer getParentBuffer() 128 { 129 return parentBuffer; 130 } 131 132 public final void setParentBuffer(Buffer b) 133 { 134 this.parentBuffer = b; 135 } 136 137 public boolean isPrimary() 138 { 139 return true; 140 } 141 142 public boolean isSecondary() 143 { 144 return false; 145 } 146 147 public boolean isPaired() 148 { 149 return isSecondary() || getSecondary() != null; 150 } 151 152 public Buffer getPrimary() 153 { 154 return null; 155 } 156 157 public Buffer getSecondary() 158 { 159 return null; 160 } 161 162 public float getSplit() 163 { 164 return 0.5F; 165 } 166 167 public void promote() 168 { 169 } 170 171 public final boolean isNewFile() 172 { 173 return isNewFile; 174 } 175 176 private final void setNewFile(boolean b) 177 { 178 isNewFile = b; 179 } 180 181 protected Buffer() 182 { 183 Editor.getBufferList().add(this); 185 } 186 187 public Buffer(int i ) 189 { 190 this(); 191 Debug.assertTrue(Editor.getBufferList().contains(this)); 192 initializeUndo(); 193 type = TYPE_NORMAL; 194 isUntitled = true; 195 ++untitledCount; 196 String name = "Untitled-" + untitledCount; 197 File directory = Editor.currentEditor().getCurrentDirectory(); 198 if (directory == null || directory.isRemote()) 199 directory = Directories.getUserHomeDirectory(); 200 setFile(File.getInstance(directory, name)); 201 autosaveEnabled = true; 202 lineSeparator = System.getProperty("line.separator"); 203 mode = PlainTextMode.getMode(); 204 formatter = mode.getFormatter(this); 205 setNewFile(true); 206 try { 207 lockWrite(); 208 } 209 catch (InterruptedException e) { 210 Log.debug(e); 211 return; } 213 try { 214 appendLine(""); 215 renumber(); 216 setLoaded(true); 217 } 218 finally { 219 unlockWrite(); 220 } 221 } 222 223 public Buffer(File file) 224 { 225 this(); 226 Debug.assertTrue(Editor.getBufferList().contains(this)); 227 initializeUndo(); 228 setFile(file); 229 type = TYPE_NORMAL; 230 autosaveEnabled = true; 231 } 232 233 public static Buffer createBuffer(File file) 234 { 235 if (file instanceof FtpFile) { 236 FtpSession session = FtpSession.getSession((FtpFile)file); 237 if (session == null) 238 return null; 239 return new RemoteBuffer((FtpFile)file, session); 240 } 241 if (file instanceof HttpFile) { 242 if (Editor.getModeList().modeAccepts(IMAGE_MODE, file.getName())) 243 return new RemoteBuffer(file); 244 if (Editor.preferences().getBooleanProperty(Property.ENABLE_WEB)) { 245 int modeId = 246 Editor.getModeList().getModeIdForFileName(file.getName()); 247 if (modeId < 0 || modeId == HTML_MODE) 248 return WebBuffer.createWebBuffer(file, null, null); 249 } 250 return new RemoteBuffer(file); 251 } 252 if (file instanceof SshFile) { 253 SshFile sshFile = (SshFile) file; 254 SshSession session = SshSession.getSession(sshFile); 255 if (session == null) 256 return null; 257 if (!session.isLocked()) { 258 Debug.bug(); 259 return null; 260 } 261 session.checkLogin(); 262 if (sshFile.getUserName() == null) { 263 Debug.bug(); 264 sshFile.setUserName(session.getUserName()); 265 } 266 if (!sshFile.getUserName().equals(session.getUserName())) { 267 Debug.bug(); 268 session.unlock(); 269 return null; 270 } 271 session.unlock(); 272 return new RemoteBuffer(file); 273 } 274 File dir = file.getParentFile(); 276 if (dir != null && dir.equals(Directories.getDraftsFolder())) { 277 Mode sendMailMode = Editor.getModeList().getMode(SEND_MAIL_MODE); 278 if (sendMailMode != null) 279 return sendMailMode.createBuffer(file); 280 } 281 return createBuffer(file, null, null); 283 } 284 285 protected static Buffer createBuffer(File file, File cache, String listing) 286 { 287 Compression compression = null; 288 int fileType = Utilities.getFileType(cache != null ? cache : file); 289 if (fileType == FILETYPE_GZIP) { 290 File uncompressed = cacheGZIP(cache != null ? cache : file); 293 if (uncompressed != null) { 294 cache = uncompressed; 295 fileType = Utilities.getFileType(cache); 296 compression = new Compression(COMPRESSION_GZIP); 297 } else 298 fileType = FILETYPE_BINARY; } 300 if (fileType == FILETYPE_JPEG || 301 Editor.getModeList().modeAccepts(IMAGE_MODE, file.getName())) { 302 Buffer buffer = 303 ImageBuffer.createImageBuffer(file, cache, listing); 304 if (buffer != null) { 305 buffer.setFileType(fileType); 306 return buffer; 307 } 308 } 309 Buffer buffer = new Buffer(file); 311 Debug.assertTrue(Editor.getBufferList().contains(buffer)); 312 buffer.setFileType(fileType); 313 buffer.setCache(cache); 314 buffer.setListing(listing); 315 buffer.setCompression(compression); 316 if (file.isLocal() && !file.isFile()) 317 buffer.setNewFile(true); 318 return buffer; 319 } 320 321 public static Buffer precreateBuffer(File file) 323 { 324 if (file == null) { 325 Debug.bug(); 326 return null; 327 } 328 if (file.isRemote()) { 329 Debug.bug(); 330 return null; 331 } 332 File dir = file.getParentFile(); 334 if (dir != null && dir.equals(Directories.getDraftsFolder())) { 335 Mode sendMailMode = Editor.getModeList().getMode(SEND_MAIL_MODE); 336 if (sendMailMode != null) 337 return sendMailMode.createBuffer(file); 338 } 339 return new Buffer(file); 341 } 342 343 private boolean initialized; 344 345 public synchronized boolean initialized() 346 { 347 return initialized; 348 } 349 350 public synchronized void setInitialized(boolean b) 351 { 352 initialized = b; 353 } 354 355 public synchronized void initialize() 356 { 357 Debug.assertTrue(!initialized); 358 final File file = getFile(); 359 if (fileType == FILETYPE_UNKNOWN) { 360 fileType = Utilities.getFileType(cache != null ? cache : file); 361 if (fileType == FILETYPE_GZIP) { 362 File uncompressed = cacheGZIP(cache != null ? cache : file); 365 if (uncompressed != null) { 366 cache = uncompressed; 367 fileType = Utilities.getFileType(cache); 368 compression = new Compression(COMPRESSION_GZIP); 369 } else 370 fileType = FILETYPE_BINARY; } 372 } 373 mode = getDefaultMode(); 374 formatter = mode.getFormatter(this); 375 if (fileType == FILETYPE_ZIP) { 376 supportsUndo = false; 377 type = TYPE_ARCHIVE; 378 readOnly = true; 379 } else if (fileType == FILETYPE_BINARY) { 380 readOnly = true; 381 } else if (fileType == FILETYPE_WORD) { 382 readOnly = true; 383 } else if (file != null) { 384 FileHistoryEntry entry = 385 FileHistory.getFileHistory().findEntry(file.netPath()); 386 if (entry != null) { 387 final String encoding = entry.getEncoding(); 389 if (encoding != null && Utilities.isSupportedEncoding(encoding)) 390 file.setEncoding(encoding); 391 mode = 393 Editor.getModeList().getModeFromModeName(entry.getMode()); 394 if (mode == null) 395 mode = Editor.getModeList().getMode(PLAIN_TEXT_MODE); 396 else if (mode.getId() == BINARY_MODE) 397 readOnly = true; 398 formatter = mode.getFormatter(this); 399 properties.putAll(entry.getProperties()); 402 } 403 } 404 if (file != null) { 405 if (file.getProtocol() == File.PROTOCOL_HTTP || 406 file.getProtocol() == File.PROTOCOL_HTTPS) 407 readOnly = true; 408 } 409 initialized = true; 410 } 411 412 public Mode getDefaultMode() 413 { 414 final File file = getFile(); 415 final ModeList modeList = Editor.getModeList(); 416 switch (fileType) { 417 case FILETYPE_XML: 418 return modeList.getMode(XML_MODE); 419 case FILETYPE_SHELLSCRIPT: { 420 Mode m = grovelModeFromFile(file); 421 if (m != null) 422 return m; 423 return modeList.getMode(SHELL_SCRIPT_MODE); 424 } 425 case FILETYPE_PERL: 426 return modeList.getMode(PERL_MODE); 427 case FILETYPE_PHP: 428 return modeList.getMode(PHP_MODE); 429 case FILETYPE_ZIP: 430 return modeList.getMode(ARCHIVE_MODE); 431 case FILETYPE_GZIP: 432 case FILETYPE_BINARY: 433 return modeList.getMode(BINARY_MODE); 434 case FILETYPE_JPEG: 435 return modeList.getMode(IMAGE_MODE); 436 case FILETYPE_WORD: 437 return modeList.getMode(WORD_MODE); 438 case FILETYPE_TEXT: 439 default: { 440 Mode m = grovelModeFromFile(file); 441 if (m == null) { 442 if (compression != null && compression.getType() == COMPRESSION_ZIP) { 443 String entryName = compression.getEntryName(); 444 if (entryName != null) 445 m = getModeForFileName(entryName); 446 } else if (file != null) { 447 m = getModeForFileName(file.getName()); 448 } 449 if (m != null && m.getId() == IMAGE_MODE) { 450 if (fileType == FILETYPE_TEXT) 451 m = modeList.getMode(PLAIN_TEXT_MODE); 452 else 453 m = modeList.getMode(BINARY_MODE); 454 } else if (m == null) { 455 if (file != null) { 456 if (file.getProtocol() == File.PROTOCOL_HTTP || 457 file.getProtocol() == File.PROTOCOL_HTTPS) 458 m = modeList.getMode(HTML_MODE); 459 } 460 if (m == null) 461 m = modeList.getMode(PLAIN_TEXT_MODE); 462 } 463 } 464 return m; 465 } 466 } 467 } 468 469 private static final Mode grovelModeFromFile(File file) 470 { 471 if (file == null) 472 return null; 473 if (!file.isLocal()) 474 return null; 475 if (!file.isFile()) 476 return null; 477 Mode mode = null; 478 try { 479 BufferedReader reader = 480 new BufferedReader (new InputStreamReader (file.getInputStream())); 481 String s = reader.readLine(); 482 if (s != null) { 483 mode = grovelModeFromString(s); 484 if (mode == null && s.startsWith("#!")) { 485 s = reader.readLine(); 487 if (s != null) 488 mode = grovelModeFromString(s); 489 } 490 } 491 reader.close(); 492 } 493 catch (IOException e) { 494 Log.error(e); 495 } 496 return mode; 497 } 498 499 private static final Mode grovelModeFromString(String s) 500 { 501 if (s != null) { 502 int begin = s.indexOf("-*-"); 503 if (begin >= 0) { 504 s = s.substring(begin + 3); 505 int end = s.indexOf("-*-"); 506 if (end >= 0) { 507 s = s.substring(0, end).trim().toLowerCase(); 508 int index = s.indexOf("mode:"); 509 String modeName; 510 if (index < 0) { 511 modeName = s; 513 } else { 514 s = s.substring(5).trim(); 516 for (end = 0; end < s.length(); end++) { 517 char c = s.charAt(end); 518 if (c == ' ' || c == '\t' || c == ';') 519 break; 520 } 521 modeName = s.substring(0, end); 522 } 523 return Editor.getModeList().getModeFromModeName(modeName); 524 } 525 } 526 } 527 return null; 528 } 529 530 public final boolean isInUse() 532 { 533 return mutex.isInUse(); 534 } 535 536 public void acquire() throws InterruptedException 537 { 538 mutex.acquire(); 539 } 540 541 public synchronized void release() 542 { 543 mutex.release(); 544 } 545 546 public boolean attempt() throws InterruptedException 547 { 548 return mutex.attempt(); 549 } 550 551 public boolean attempt(long msecs) throws InterruptedException 552 { 553 return mutex.attempt(msecs); 554 } 555 556 public final boolean isLocked() 557 { 558 return isInUse(); 559 } 560 561 public synchronized boolean lock() 562 { 563 try { 564 return attempt(); 565 } 566 catch (InterruptedException e) { 567 return false; 568 } 569 } 570 571 public synchronized void unlock() 572 { 573 release(); 574 } 575 576 public final void lockRead() throws InterruptedException 577 { 578 rwlock.lockRead(); 579 } 580 581 public final void unlockRead() 582 { 583 rwlock.unlockRead(); 584 } 585 586 public final void lockWrite() throws InterruptedException 587 { 588 rwlock.lockWrite(); 589 } 590 591 public final void unlockWrite() 592 { 593 rwlock.unlockWrite(); 594 } 595 596 public final boolean isWriteLocked() 597 { 598 return rwlock.isWriteLocked(); 599 } 600 601 public boolean isVisible() 602 { 603 for (EditorIterator it = new EditorIterator(); it.hasNext();) 604 if (it.nextEditor().getBuffer() == this) 605 return true; 606 607 return false; 608 } 609 610 public void setWaitCursor() 611 { 612 for (EditorIterator it = new EditorIterator(); it.hasNext();) { 613 Editor ed = it.nextEditor(); 614 if (ed.getBuffer() == this) 615 ed.setWaitCursor(); 616 } 617 } 618 619 public void setDefaultCursor() 620 { 621 for (EditorIterator it = new EditorIterator(); it.hasNext();) { 622 Editor ed = it.nextEditor(); 623 if (ed.getBuffer() == this) 624 ed.setDefaultCursor(); 625 } 626 } 627 628 public final PropertyList getProperties() 629 { 630 return properties; 631 } 632 633 public final void setCache(File file) 634 { 635 cache = file; 636 } 637 638 public final File getCache() 639 { 640 return cache; 641 } 642 643 public final Formatter getFormatter() 644 { 645 return formatter; 646 } 647 648 public final void setFormatter(Formatter formatter) 649 { 650 this.formatter = formatter; 651 } 652 653 public boolean isReadOnly() 654 { 655 return readOnly || forceReadOnly; 656 } 657 658 private boolean busy; 659 660 public synchronized final boolean isBusy() 661 { 662 return busy; 663 } 664 665 public synchronized final void setBusy(boolean b) 666 { 667 busy = b; 668 } 669 670 public final BackgroundProcess getBackgroundProcess() 671 { 672 return backgroundProcess; 673 } 674 675 public final void setBackgroundProcess(BackgroundProcess backgroundProcess) 676 { 677 this.backgroundProcess = backgroundProcess; 678 } 679 680 public File getCurrentDirectory() 681 { 682 return getFile() != null ? getFile().getParentFile() : Directories.getUserHomeDirectory(); 683 } 684 685 public File getCompletionDirectory() 687 { 688 final File file = getFile(); 689 if (file != null) { 690 if (file.isLocal() || file instanceof SshFile) 691 return file.isDirectory() ? file : file.getParentFile(); 692 } 693 return Directories.getUserHomeDirectory(); 694 } 695 696 public View getInitialView() 697 { 698 View view = new View(); 699 view.setDot(getInitialDotPos()); 700 return view; 701 } 702 703 private int initialLineNumber; 704 private int initialOffset; 705 706 public Position getInitialDotPos() 707 { 708 Line line = getLine(initialLineNumber); 709 if (line == null) { 710 line = getFirstLine(); 711 return line != null ? new Position(line, 0) : null; 712 } 713 return new Position(line, Math.min(initialOffset, line.length())); 714 } 715 716 public void setInitialDotPos(int lineNumber, int offset) 717 { 718 initialLineNumber = lineNumber; 719 initialOffset = offset; 720 } 721 722 private long lastActivated; 723 724 public final long getLastActivated() 725 { 726 return lastActivated; 727 } 728 729 public final void setLastActivated(long l) 730 { 731 lastActivated = l; 732 } 733 734 public final int getLineCount() 735 { 736 return lineCount; 737 } 738 739 public final int getModCount() 740 { 741 return modCount; 742 } 743 744 public final synchronized void setModCount(int count) 745 { 746 if (count != modCount) { 747 modCount = count; 748 srText = null; 749 } 750 } 751 752 public final synchronized void incrementModCount() 753 { 754 ++modCount; 755 srText = null; 756 } 757 758 public final void setModCountWhenLastSaved(int count) 759 { 760 autosaveModCount = count; 761 } 762 763 public final KeyMap getKeyMapForMode() 764 { 765 return mode.getKeyMap(); 767 } 768 769 public final int getFileType() 770 { 771 return fileType; 772 } 773 774 private final void setFileType(int fileType) 775 { 776 this.fileType = fileType; 777 } 778 779 private static File cacheGZIP(File f) 780 { 781 try { 782 File tempFile = Utilities.getTempFile(); 783 if (tempFile != null) { 784 InputStream in = new GZIPInputStream (f.getInputStream()); 785 if (in != null) { 786 OutputStream out = tempFile.getOutputStream(); 787 byte[] buf = new byte[4096]; 788 int bytesRead; 789 while ((bytesRead = in.read(buf)) > 0) 790 out.write(buf, 0, bytesRead); 791 out.close(); 792 in.close(); 793 return tempFile; 794 } 795 } 796 } 797 catch (IOException e) { 798 Log.error(e); 799 } 800 return null; 801 } 802 803 public void changeFile(File f) 805 { 806 if (f == null) 807 return; 808 String newName = f.canonicalPath(); 809 if (newName == null) 810 return; 811 File oldFile = getFile(); 812 String oldName = oldFile != null ? oldFile.canonicalPath() : null; 813 setFile(f); 814 setCompression(null); 815 type = TYPE_NORMAL; 816 title = null; 817 Mode oldMode = mode; 818 if (oldMode == PlainTextMode.getMode()) { 819 setModeFromFilename(newName); 820 if (mode != oldMode) { 821 needsParsing = true; 823 824 if (formatter != null) 826 formatter.parseBuffer(); 827 } 828 } 829 readOnly = false; 830 isUntitled = false; 831 if (oldName != null) 832 Autosave.rename(oldName, newName); 833 Editor.getBufferList().modified(); 834 Sidebar.setUpdateFlagInAllFrames(SIDEBAR_BUFFER_LIST_CHANGED); 835 for (EditorIterator it = new EditorIterator(); it.hasNext();) { 836 Editor editor = it.nextEditor(); 837 if (editor.getBuffer() == this) { 838 editor.updateLocation(); 839 editor.getDisplay().repaint(); 840 } 841 } 842 } 843 844 protected void setModeFromFilename(String filename) 845 { 846 mode = Editor.getModeList().getModeForFileName(filename); 847 if (mode == null) 848 mode = Editor.getModeList().getMode(PLAIN_TEXT_MODE); 849 formatter = mode.getFormatter(this); 850 } 851 852 private static Mode getModeForFileName(String filename) 853 { 854 if (filename.endsWith(".gz")) 855 filename = filename.substring(0, filename.length() - 3); 856 857 int index = filename.lastIndexOf('/'); 860 if (index >= 0) 861 filename = filename.substring(index + 1); 862 863 if (Platform.isPlatformWindows()) { 865 index = filename.lastIndexOf('\\'); 866 if (index >= 0) 867 filename = filename.substring(index + 1); 868 } 869 870 return Editor.getModeList().getModeForFileName(filename); 871 } 872 873 public final void setMode(Mode mode) 874 { 875 this.mode = mode; 876 formatter = mode.getFormatter(this); 877 } 878 879 public void changeMode(Mode newMode) 880 { 881 final int oldModeId = mode.getId(); 882 if (oldModeId == DIRECTORY_MODE) 883 return; 884 final int newModeId = newMode.getId(); 885 if (newModeId != oldModeId) { 886 boolean reloading = newModeId == BINARY_MODE || oldModeId == BINARY_MODE; 888 mode = newMode; 889 formatter = mode.getFormatter(this); 890 if (reloading) { 891 reload(); 892 if (newModeId == IMAGE_MODE) 893 return; 894 } 895 896 if (newModeId == BINARY_MODE) { 897 readOnly = true; 898 } else { 899 final File file = getFile(); 900 if (file != null && !file.isRemote() && file.isFile()) 901 readOnly = !file.canWrite(); 902 } 903 904 formatter.parseBuffer(); 905 906 for (EditorIterator it = new EditorIterator(); it.hasNext();) { 907 Editor ed = it.nextEditor(); 908 if (ed.getBuffer() == this) { 909 if (reloading) { 910 ed.setDot(getFirstLine(), 0); 911 ed.setMark(null); 912 ed.setTopLine(getFirstLine()); 913 ed.moveCaretToDotCol(); 914 ed.updateLocation(); 915 } 916 ed.setUpdateFlag(REPAINT); 917 ed.updateDisplay(); 918 } 919 } 920 } 921 } 922 923 public final String getCommentStart() 924 { 925 return mode.getCommentStart(); 926 } 927 928 public final String getCommentEnd() 929 { 930 return mode.getCommentEnd(); 931 } 932 933 private long lastModified; 934 935 public final long getLastModified() 936 { 937 return lastModified; 938 } 939 940 public final void setLastModified(long lastModified) 941 { 942 this.lastModified = lastModified; 943 } 944 945 protected void loadFile(File toBeLoaded) 946 { 947 try { 948 int modeId = getModeId(); 949 if (modeId == ARCHIVE_MODE || modeId == WORD_MODE || modeId == XML_MODE) 950 mode.loadFile(this, toBeLoaded); 951 else { 952 final String encoding = toBeLoaded.getEncoding(); 953 load(toBeLoaded.getInputStream(), encoding); 954 if (encoding != null) 955 saveProperties(); } 957 } 958 catch (IOException e) { 959 Log.error(e); 960 } 961 if (getFirstLine() == null) { 963 appendLine(""); 964 lineSeparator = System.getProperty("line.separator"); 965 } 966 final File file = getFile(); 967 if (file != null && file.getProtocol() != File.PROTOCOL_HTTP) 968 lastModified = toBeLoaded.lastModified(); 969 renumberOriginal(); 970 } 971 972 public int load() 973 { 974 if (!isLoaded()) { 975 try { 976 lockWrite(); 977 } 978 catch (InterruptedException e) { 979 Log.error(e); 980 return LOAD_FAILED; 981 } 982 try { 983 final File file = getFile(); 984 final File toBeLoaded = cache != null ? cache : file; 985 if (toBeLoaded.isFile()) { 986 Editor editor = Editor.currentEditor(); 987 FastStringBuffer sb = new FastStringBuffer("Loading"); 988 if (compression != null) { 989 if (compression.getType() == COMPRESSION_ZIP) { 990 String entryName = compression.getEntryName(); 991 if (entryName != null) { 992 sb.append(' '); 993 sb.append(entryName); 994 } 995 } 996 } else if (file != null) { 997 sb.append(' '); 998 sb.append(file.getName()); 999 } 1000 sb.append("..."); 1001 editor.status(sb.toString()); 1002 Debug.assertTrue(mode != null); 1003 Debug.assertTrue(toBeLoaded != null); 1004 loadFile(toBeLoaded); 1005 if (toBeLoaded == cache && file != null && !file.isRemote()) 1009 lastModified = file.lastModified(); 1010 formatter.parseBuffer(); 1011 checkCVS(); 1012 sb.append("done"); 1013 editor.status(sb.toString()); 1014 } else { 1015 if (getFirstLine() == null) { 1017 appendLine(""); 1018 lineSeparator = System.getProperty("line.separator"); 1019 } 1020 renumberOriginal(); 1021 setModeFromFilename(file.canonicalPath()); 1022 setLoaded(true); 1023 } 1024 } 1025 catch (OutOfMemoryError e) { 1026 _empty(); 1027 throw e; 1028 } 1029 finally { 1030 unlockWrite(); 1031 } 1032 } 1033 return LOAD_COMPLETED; 1034 } 1035 1036 private void reloadSucceeded() 1037 { 1038 Debug.assertTrue(!rwlock.isWriteLocked()); 1039 Debug.assertTrue(SwingUtilities.isEventDispatchThread()); 1040 for (EditorIterator it = new EditorIterator(); it.hasNext();) { 1041 Editor ed = it.nextEditor(); 1042 if (ed.getBuffer() != this) 1043 continue; 1044 View view = ed.getView(this); 1045 if (view != null) { 1046 Line dotLine = getLine(view.getDotLineNumber()); 1047 if (dotLine == null) 1048 dotLine = getFirstLine(); 1049 if (dotLine != null) { 1050 ed.setDot(dotLine, 0); 1051 ed.moveCaretToDotCol(); 1052 } 1053 Line topLine = getLine(view.getTopLineNumber()); 1054 if (topLine == null) 1055 topLine = dotLine; 1056 ed.setTopLine(topLine); 1057 } else { 1058 ed.setDot(getFirstLine(), 0); 1059 ed.moveCaretToDotCol(); 1060 ed.setTopLine(getFirstLine()); 1061 } 1062 ed.setUpdateFlag(REPAINT); 1063 ed.updateDisplay(); 1064 } 1065 Sidebar.setUpdateFlagInAllFrames(SIDEBAR_REPAINT_BUFFER_LIST); 1068 } 1069 1070 private void reloadFailed() 1071 { 1072 MessageDialog.showMessageDialog("Reload failed", "Error"); 1073 } 1074 1075 public void reload() 1076 { 1077 final File file = getFile(); 1078 if (file == null) 1079 return; 1080 switch (file.getProtocol()) { 1081 case File.PROTOCOL_FTP: 1082 reloadFtp((FtpFile) file); 1083 return; 1084 case File.PROTOCOL_HTTP: 1085 case File.PROTOCOL_HTTPS: 1086 reloadHttp((HttpFile) file); 1087 return; 1088 case File.PROTOCOL_FILE: 1089 if (file.isFile()) { 1090 if (getModeId() == ARCHIVE_MODE) { 1092 empty(); 1093 resetUndo(); 1094 mode.loadFile(this, file); 1095 } else 1096 reloadLocal(file); 1097 } 1098 reloadSucceeded(); 1099 return; 1100 default: 1101 Debug.assertTrue(false); 1102 break; 1103 } 1104 } 1105 1106 private void reloadFtp(FtpFile file) 1108 { 1109 Log.debug("reloadFtp"); 1110 Debug.assertTrue(SwingUtilities.isEventDispatchThread()); 1111 FtpSession session = FtpSession.getSession(file); 1112 final FtpLoadProcess ftpLoadProcess = new FtpLoadProcess(this, file, session); 1113 Runnable successRunnable = new Runnable () { 1114 public void run() 1115 { 1116 File newCache = ftpLoadProcess.getCache(); 1117 if (newCache != null) { 1118 Log.debug("newCache != null"); 1119 if (cache != null && cache.isFile()) 1120 cache.delete(); 1121 cache = newCache; 1122 reloadLocal(cache); 1123 } else { 1124 setLoaded(true); 1126 } 1127 setBusy(false); 1128 reloadSucceeded(); 1129 } 1130 }; 1131 ErrorRunnable errorRunnable = new ErrorRunnable("Reload failed") { 1132 public void run() 1133 { 1134 setBusy(false); 1135 reloadFailed(); 1136 } 1137 }; 1138 ftpLoadProcess.setProgressNotifier(new StatusBarProgressNotifier(this)); 1139 ftpLoadProcess.setSuccessRunnable(successRunnable); 1140 ftpLoadProcess.setErrorRunnable(errorRunnable); 1141 ftpLoadProcess.start(); 1142 } 1143 1144 private void reloadHttp(HttpFile file) 1145 { 1146 Log.debug("reloadHttp"); 1147 Debug.assertTrue(SwingUtilities.isEventDispatchThread()); 1148 final HttpLoadProcess httpLoadProcess = new HttpLoadProcess(this, file); 1149 Runnable successRunnable = new Runnable () { 1150 public void run() 1151 { 1152 File newCache = httpLoadProcess.getCache(); 1153 if (newCache != null) { 1154 Log.debug("newCache != null"); 1155 if (cache != null && cache.isFile()) 1156 cache.delete(); 1157 cache = newCache; 1158 reloadLocal(cache); 1159 } else { 1160 setLoaded(true); 1162 } 1163 setBusy(false); 1164 reloadSucceeded(); 1165 } 1166 }; 1167 ErrorRunnable errorRunnable = new ErrorRunnable("Reload failed") { 1168 public void run() 1169 { 1170 setBusy(false); 1171 reloadFailed(); 1172 } 1173 }; 1174 httpLoadProcess.setProgressNotifier(new StatusBarProgressNotifier(this)); 1175 httpLoadProcess.setSuccessRunnable(successRunnable); 1176 httpLoadProcess.setErrorRunnable(errorRunnable); 1177 setBusy(true); 1178 new Thread (httpLoadProcess).start(); 1179 } 1180 1181 private void reloadLocal(File file) 1182 { 1183 try { 1184 lockWrite(); 1185 } 1186 catch (InterruptedException e) { 1187 Log.error(e); 1188 return; 1189 } 1190 try { 1191 empty(); 1192 loadFile(file); 1193 formatter.parseBuffer(); 1194 } 1195 finally { 1196 unlockWrite(); 1197 } 1198 unmodified(); 1199 deleteAutosaveFile(); 1200 resetUndo(); 1201 } 1202 1203 public synchronized void kill() 1204 { 1205 BufferList bufferList = Editor.getBufferList(); 1206 if (!bufferList.contains(this)) { 1207 Debug.bug("buffer.kill() buffer not in list"); 1208 return; 1209 } 1210 1211 Marker.invalidateMarkers(this); 1212 1213 Buffer buf = bufferList.getPreviousPrimaryBuffer(this); 1214 if (buf != null && buf.isPaired()) { 1215 Buffer secondary = buf.getSecondary(); 1216 if (secondary != null) { 1217 if (secondary.getLastActivated() > buf.getLastActivated()) 1218 buf = secondary; 1219 } 1220 } 1221 if (buf == null) 1222 buf = new Directory(getCurrentDirectory()); 1223 ArrayList editors = new ArrayList (); 1225 for (EditorIterator it = new EditorIterator(); it.hasNext();) 1226 editors.add(it.next()); 1227 for (Iterator it = editors.iterator(); it.hasNext();) { 1228 Editor ed = (Editor) it.next(); 1229 if (Editor.getEditorList().contains(ed)) { 1231 if (ed.getBuffer() == this) 1232 ed.switchToBuffer(buf); 1233 ed.removeView(this); 1234 } 1235 } 1236 deleteAutosaveFile(); 1237 bufferList.remove(this); 1238 dispose(); 1239 Sidebar.setUpdateFlagInAllFrames(SIDEBAR_BUFFER_LIST_CHANGED | 1240 SIDEBAR_MODIFIED_BUFFER_COUNT); 1241 } 1242 1243 public synchronized void relink() 1244 { 1245 BufferList bufferList = Editor.getBufferList(); 1246 if (bufferList.contains(this)) { 1247 Debug.bug(); 1248 return; 1249 } 1250 bufferList.add(this); 1251 } 1252 1253 private Thread startTaggerThread(int priority) 1254 { 1255 if (mode != null) { 1256 Tagger tagger = mode.getTagger(this); 1257 if (tagger != null) { 1258 Thread thread = new Thread (tagger); 1259 thread.setPriority(priority); 1260 thread.setDaemon(true); 1261 thread.setName("tagger " + getFile().getName()); 1262 thread.start(); 1263 return thread; 1264 } 1265 } 1266 return null; 1267 } 1268 1269 public boolean isTaggable() 1270 { 1271 final File file = getFile(); 1272 if (file == null) 1273 return false; 1274 if (file.isRemote()) 1275 return false; 1276 return mode.isTaggable(); 1277 } 1278 1279 public List getTags(boolean update) 1281 { 1282 List tags = getTags(); 1283 if (tags != null) 1284 return tags; 1285 if (mode != null) { 1286 Tagger tagger = mode.getTagger(this); 1287 if (tagger != null) 1288 tagger.run(); 1289 } 1290 return getTags(); 1291 } 1292 1293 public boolean saveToCache() 1294 { 1295 if (lineSeparator == null) 1296 lineSeparator = System.getProperty("line.separator"); 1297 final File tempFile = Utilities.getTempFile(); 1298 if (tempFile != null) { 1299 File file = getFile(); 1300 if (file != null) 1301 tempFile.setEncoding(file.getEncoding()); 1302 if (writeFile(tempFile)) { 1303 final File oldCache = cache; 1304 cache = tempFile; 1305 if (oldCache != null && oldCache.isFile()) 1306 oldCache.delete(); 1307 return true; 1308 } 1309 } 1310 return false; 1311 } 1312 1313 private boolean maybeWriteBackupFromCache() 1314 { 1315 if (cache == null) { 1316 Log.error("maybeWriteBackupFromCache cache is null"); 1317 return false; 1318 } 1319 if (!cache.isFile()) { 1320 Log.error("maybeWriteBackupFromCache cache is not a file"); 1321 return false; 1322 } 1323 if (!cache.canRead()) { 1324 Log.error("maybeWriteBackupFromCache cache is not readable"); 1325 return false; 1326 } 1327 if (backedUp) 1328 return true; File file = getFile(); 1330 if (file == null) { 1331 Debug.bug(); 1332 return false; 1333 } 1334 String name = file.getName(); 1335 if (compression != null && compression.getType() == COMPRESSION_GZIP) { 1336 if (name.endsWith(".gz")) 1337 name = name.substring(0, name.length() - 3); 1338 } 1339 return backedUp = 1340 Utilities.makeBackup(cache, name, false); 1341 } 1342 1343 public boolean save() 1344 { 1345 Debug.assertTrue(SwingUtilities.isEventDispatchThread()); 1346 if (!isModified()) 1347 return true; 1348 addUndoBoundary(); 1349 boolean succeeded = false; 1350 final File file = getFile(); 1351 if (file != null) { 1352 if (file.isLocal()) 1353 succeeded = saveLocal(file); 1354 if (file instanceof FtpFile) 1355 succeeded = saveFtp(); 1356 if (file instanceof SshFile) 1357 succeeded = saveSsh((SshFile)file); 1358 } 1359 if (succeeded && Editor.isLispInitialized()) 1360 LispAPI.invokeAfterSaveHook(this); 1361 return succeeded; 1362 } 1363 1364 private boolean saveLocal(final File file) 1365 { 1366 Debug.assertTrue(file.isLocal()); 1367 1368 if (compression != null && compression.getType() == COMPRESSION_GZIP) 1369 return saveLocalCompressed(file); 1370 1371 try { 1372 writeBuffer(); 1373 renumberOriginal(); 1374 saved(); 1375 lastModified = file.lastModified(); 1376 } 1377 catch (SaveException e) { 1378 final String where = "Save"; 1379 String message = e.getMessage(); 1380 if (message != null) 1382 MessageDialog.showMessageDialog(message, where); 1383 message = "Unable to save " + file.canonicalPath(); 1385 MessageDialog.showMessageDialog(message, where); 1386 return false; 1387 } 1388 1389 if (isTaggable()) 1390 Editor.getTagFileManager().addToQueue(file.getParentFile(), mode); 1391 startTaggerThread(Thread.MIN_PRIORITY); 1392 boolean repaint = false; 1393 final ModeList modeList = Editor.getModeList(); 1394 if (file.equals(Preferences.getPreferencesFile())) { 1395 Editor.loadPreferences(); 1397 if (Editor.preferences().getBooleanProperty(Property.AUTO_RELOAD_KEY_MAPS)) { 1398 KeyMap.reloadKeyMaps(); 1400 } 1401 repaint = true; 1402 } else { 1403 if (Editor.preferences().getBooleanProperty(Property.AUTO_RELOAD_KEY_MAPS)) { 1404 synchronized (modeList) { 1406 for (Iterator it = modeList.iterator(); it.hasNext();) { 1407 ModeListEntry entry = (ModeListEntry) it.next(); 1408 Mode mode = entry.getMode(false); 1409 if (mode != null) { 1410 if (file.equals(mode.getKeyMapFile())) 1411 mode.deleteKeyMap(); 1412 } 1413 } 1414 } 1415 if (file.equals(KeyMap.getGlobalKeyMapFile())) 1417 KeyMap.deleteGlobalKeyMap(); 1418 } 1419 } 1420 String theme = Editor.preferences().getStringProperty(Property.THEME); 1422 if (theme != null) { 1423 if (Utilities.isFilenameAbsolute(theme)) { 1424 if (file.canonicalPath().equals(File.getInstance(theme).canonicalPath())) { 1425 Editor.loadPreferences(); 1426 repaint = true; 1427 } 1428 } else if (file.getName().equals(theme)) { 1429 Editor.loadPreferences(); 1430 repaint = true; 1431 } 1432 } 1433 1434 if (file.equals(Editor.getAliasesFile())) 1436 Editor.reloadAliases(); 1437 1438 File parent = file.getParentFile(); 1440 if (parent != null && parent.equals(Directories.getRegistersDirectory())) { 1441 Buffer buf = Registers.findListRegistersBuffer(); 1442 if (buf != null) 1443 buf.reload(); 1444 } 1445 1446 checkCVS(); 1447 1448 if (repaint) { 1449 for (BufferIterator it = new BufferIterator(); it.hasNext();) { 1451 Buffer buf = it.nextBuffer(); 1452 if (buf.getFormatter() != null) 1453 buf.getFormatter().reset(); 1454 } 1455 Display.initializeStaticValues(); 1456 for (int i = 0; i < Editor.getFrameCount(); i++) { 1457 Frame f = Editor.getFrame(i); 1458 if (f != null) { 1459 f.resetDisplay(); 1460 f.repaint(); 1461 } 1462 } 1463 Editor.restoreFocus(); 1464 } 1465 return true; 1466 } 1467 1468 private boolean saveLocalCompressed(File file) 1469 { 1470 final String dialogTitle = "Save"; 1471 final Editor editor = Editor.currentEditor(); 1472 if (!maybeWriteBackupFromCache()) { 1474 FastStringBuffer sb = 1475 new FastStringBuffer("Unable to write backup file for "); 1476 sb.append(file.getName()); 1477 sb.append(". Save anyway?"); 1478 if (!editor.confirm(dialogTitle, sb.toString())) 1479 return false; 1480 } 1481 if (!saveToCache()) { 1482 FastStringBuffer sb = 1483 new FastStringBuffer("Unable to write temporary file for "); 1484 sb.append(file.getName()); 1485 MessageDialog.showMessageDialog(sb.toString(), dialogTitle); 1486 return false; 1487 } 1488 if (!compress(cache, file)) 1489 return false; 1490 saved(); 1491 lastModified = file.lastModified(); 1492 return true; 1493 } 1494 1495 private boolean compress(File source, File destination) 1496 { 1497 if (source == null) { 1498 Debug.bug(); 1499 return false; 1500 } 1501 if (destination == null) { 1502 Debug.bug(); 1503 return false; 1504 } 1505 if (!source.isFile()) { 1506 Debug.bug(); 1507 return false; 1508 } 1509 final File tempFile = Utilities.getTempFile(destination.getParentFile()); 1510 try { 1511 final int bufSize = 4096; 1512 BufferedInputStream in = 1513 new BufferedInputStream (source.getInputStream()); 1514 GZIPOutputStream out = 1515 new GZIPOutputStream (new BufferedOutputStream (tempFile.getOutputStream()), 1516 bufSize); 1517 byte[] buffer = new byte[bufSize]; 1518 while (true) { 1519 int bytesRead = in.read(buffer, 0, bufSize); 1520 if (bytesRead > 0) 1521 out.write(buffer, 0, bytesRead); 1522 else 1523 break; 1524 } 1525 in.close(); 1526 out.flush(); 1527 out.close(); 1528 return Utilities.deleteRename(tempFile, destination); 1529 } 1530 catch (IOException e) { 1531 Log.error(e); 1532 return false; 1533 } 1534 } 1535 1536 private boolean saveFtp() 1537 { 1538 Debug.assertTrue(SwingUtilities.isEventDispatchThread()); 1539 Debug.assertTrue(getFile() instanceof FtpFile); 1540 final FtpFile file = (FtpFile) getFile(); 1541 final FtpSession session = FtpSession.getSession(file); 1542 if (session == null) 1543 return false; 1544 Debug.assertTrue(session.isLocked()); 1545 if (!lock()) { 1546 session.unlock(); 1547 MessageDialog.showMessageDialog("Buffer is busy", file.netPath()); 1548 return false; 1549 } 1550 Debug.assertTrue(isLocked()); 1551 final Editor editor = Editor.currentEditor(); 1552 editor.setWaitCursor(); 1553 if (!maybeWriteBackupFromCache()) { 1555 editor.setDefaultCursor(); 1556 String message = "Unable to write backup file for " + 1557 file.getName() + ". Save anyway?"; 1558 if (!editor.confirm("Save", message)) { 1559 unlock(); 1560 session.unlock(); 1561 return false; 1562 } 1563 editor.setWaitCursor(); 1564 } 1565 if (!saveToCache()) { 1566 unlock(); 1567 session.unlock(); 1568 editor.setDefaultCursor(); 1569 String message = "Unable to write temporary file for " + 1570 file.getName(); 1571 MessageDialog.showMessageDialog(message, "Save"); 1572 return false; 1573 } 1574 final FtpSaveProcess saveProcess; 1575 if (compression != null && compression.getType() == COMPRESSION_GZIP) { 1576 final File tempFile = Utilities.getTempFile(); 1577 if (!compress(cache, tempFile)) { 1578 String message = "Unable to compress temporary file for " + 1579 file.getName(); 1580 MessageDialog.showMessageDialog(message, "Save"); 1581 return false; 1582 } 1583 saveProcess = new FtpSaveProcess(this, tempFile, file, session); 1584 } else 1585 saveProcess = new FtpSaveProcess(this, cache, file, session); 1586 saveProcess.setConfirmIfDestinationChanged(true); 1587 saveProcess.setTitle("Save"); 1588 final Runnable successRunnable = new Runnable () { 1589 public void run() 1590 { 1591 saved(); 1592 setListing(saveProcess.getListing()); 1593 for (EditorIterator it = new EditorIterator(); it.hasNext();) { 1594 Editor ed = it.nextEditor(); 1595 if (ed.getBuffer() == Buffer.this) 1596 ed.setDefaultCursor(); 1597 } 1598 Sidebar.repaintBufferListInAllFrames(); 1599 } 1600 }; 1601 saveProcess.setSuccessRunnable(successRunnable); 1602 Debug.assertTrue(isLocked()); 1603 saveProcess.start(); 1604 return true; 1605 } 1606 1607 private boolean saveSsh(final SshFile file) 1608 { 1609 final String title = "Save"; 1610 final Editor editor = Editor.currentEditor(); 1611 boolean succeeded = false; 1612 String message = null; 1613 1614 if (!maybeWriteBackupFromCache()) { 1616 message = "Unable to write backup file for " + file.getName() + 1617 ". Save anyway?"; 1618 if (!editor.confirm(title, message)) 1619 return false; 1620 } 1621 1622 if (!saveToCache()) { 1623 message = "Unable to write temporary file for " + file.getName(); 1624 MessageDialog.showMessageDialog(message, title); 1625 return false; 1626 } 1627 1628 Ssh ssh = new Ssh(); 1629 1630 if (compression != null && compression.getType() == COMPRESSION_GZIP) { 1631 final File tempFile = Utilities.getTempFile(); 1632 if (!compress(cache, tempFile)) { 1633 message = "Unable to compress temporary file for " + 1634 file.getName(); 1635 MessageDialog.showMessageDialog(message, "Save"); 1636 return false; 1637 } 1638 succeeded = ssh.copy(tempFile, file); 1639 } else 1640 succeeded = ssh.copy(cache, file); 1641 1642 if (!succeeded) 1643 message = ssh.getErrorText(); 1644 1645 if (succeeded) { 1646 saved(); 1647 } else { 1648 if (message != null) 1650 MessageDialog.showMessageDialog(message, title); 1651 1652 message = "Unable to save " + file.canonicalPath(); 1654 MessageDialog.showMessageDialog(message, title); 1655 } 1656 1657 return succeeded; 1658 } 1659 1660 public void saveAs(File destination) 1661 { 1662 File file = getFile(); 1663 if (file != null) 1664 destination.setEncoding(file.getEncoding()); 1665 if (destination.isLocal()) 1666 saveAsLocal(destination); 1667 else if (destination instanceof FtpFile) 1668 saveAsFtp((FtpFile)destination); 1669 else 1670 MessageDialog.showMessageDialog("Invalid destination", "Save As"); 1671 } 1672 1673 private boolean saveAsLocal(File destination) 1674 { 1675 Debug.assertTrue(SwingUtilities.isEventDispatchThread()); 1676 if (destination == null) 1677 return false; 1678 if (destination.isDirectory()) { 1679 final String prompt = 1680 destination.canonicalPath().concat(" is a directory"); 1681 MessageDialog.showMessageDialog(prompt, "Save As"); 1682 return false; 1683 } 1684 setBusy(true); 1685 Editor.currentEditor().setWaitCursor(); 1686 boolean succeeded = false; 1687 String message = null; 1688 if (lineSeparator == null) 1689 lineSeparator = System.getProperty("line.separator"); 1690 final File tempFile = Utilities.getTempFile(destination.getParent()); 1691 if (tempFile == null) { 1692 message = "Unable to create temporary file for " + 1693 destination.canonicalPath(); 1694 } else { 1695 tempFile.setEncoding(destination.getEncoding()); 1696 if (writeFile(tempFile)) { 1697 Log.debug("buffer written to " + tempFile.canonicalPath()); 1698 if (Utilities.makeBackup(destination, false)) { 1699 destination.delete(); 1700 if (tempFile.renameTo(destination)) { 1701 succeeded = true; 1702 } else { 1703 Log.error("unable to rename " + 1704 tempFile.canonicalPath() + " to " + 1705 destination.canonicalPath()); 1706 message = "Unable to rename temporary file"; 1707 } 1708 } else { 1709 Log.error("backup failed"); 1710 message = "Unable to write backup file for " + 1711 destination.canonicalPath(); 1712 } 1713 } else { 1714 Log.error("writeFile failed"); 1715 message = "Unable to create temporary file in " + 1716 tempFile.getParent(); 1717 } 1718 } 1719 setBusy(false); 1720 if (succeeded) { 1721 saved(); 1722 changeFile(destination); 1723 setLastModified(getFile().lastModified()); 1724 checkCVS(); 1725 final String encoding = destination.getEncoding(); 1726 if (encoding != null) 1727 saveProperties(); if (isTaggable()) 1729 Editor.getTagFileManager().addToQueue( 1730 destination.getParentFile(), 1731 mode); 1732 Sidebar.setUpdateFlagInAllFrames(SIDEBAR_REPAINT_BUFFER_LIST); 1733 } else { 1734 if (message != null) 1736 MessageDialog.showMessageDialog(message, "Save As"); 1737 MessageDialog.showMessageDialog("Save failed", "Save As"); 1738 } 1739 return succeeded; 1740 } 1741 1742 private void saveAsFtp(final FtpFile destination) 1743 { 1744 Debug.assertTrue(SwingUtilities.isEventDispatchThread()); 1745 FtpSession session = FtpSession.getSession(destination); 1746 if (session == null) 1747 return; 1748 Debug.assertTrue(session.isLocked()); 1749 if (!lock()) { 1750 session.unlock(); 1751 MessageDialog.showMessageDialog("Buffer is busy", 1752 getFile().netPath()); 1753 return; 1754 } 1755 final Editor editor = Editor.currentEditor(); 1756 editor.setWaitCursor(); 1757 if (!saveToCache()) { 1758 unlock(); 1759 session.unlock(); 1760 editor.setDefaultCursor(); 1761 String message = "Unable to write temporary file for " + 1762 destination.netPath(); 1763 MessageDialog.showMessageDialog(message, "Save As"); 1764 return; 1765 } 1766 final FtpSaveProcess saveProcess = 1767 new FtpSaveProcess(this, cache, destination, session); 1768 saveProcess.setConfirmOverwrite(true); 1769 saveProcess.setTitle("Save As"); 1770 final Runnable successRunnable = new Runnable () { 1771 public void run() 1772 { 1773 saved(); 1774 changeFile(destination); 1775 setListing(saveProcess.getListing()); 1776 Sidebar.setUpdateFlagInAllFrames(SIDEBAR_REPAINT_BUFFER_LIST); 1777 for (EditorIterator it = new EditorIterator(); it.hasNext();) { 1778 Editor ed = it.nextEditor(); 1779 if (ed.getBuffer() == Buffer.this) 1780 ed.setDefaultCursor(); 1781 } 1782 Sidebar.repaintBufferListInAllFrames(); 1783 } 1784 }; 1785 saveProcess.setSuccessRunnable(successRunnable); 1786 Debug.assertTrue(isLocked()); 1787 saveProcess.start(); 1788 } 1789 1790 public void saveCopy(File destination) 1791 { 1792 if (destination.isLocal()) 1793 saveCopyLocal(destination); 1794 else if (destination instanceof FtpFile) 1795 saveCopyFtp((FtpFile)destination); 1796 else 1797 MessageDialog.showMessageDialog("Invalid destination", "Save Copy"); 1798 } 1799 1800 private void saveCopyLocal(File destination) 1801 { 1802 boolean succeeded = false; 1803 String message = null; 1804 if (lineSeparator == null) 1805 lineSeparator = System.getProperty("line.separator"); 1806 File tempFile = Utilities.getTempFile(destination.getParent()); 1807 if (tempFile == null) { 1808 message = "Unable to create temporary file for " + 1809 destination.canonicalPath(); 1810 } else { 1811 tempFile.setEncoding(destination.getEncoding()); 1812 if (writeFile(tempFile)) { 1813 Log.debug("buffer written to " + tempFile.canonicalPath()); 1814 if (Utilities.makeBackup(destination, false)) { 1815 destination.delete(); 1816 if (tempFile.renameTo(destination)) { 1817 succeeded = true; 1818 } else { 1819 Log.error("unable to rename " + 1820 tempFile.canonicalPath() + " to " + 1821 destination.canonicalPath()); 1822 message = "Unable to rename temporary file"; 1823 } 1824 } else { 1825 Log.error("backup failed"); 1826 message = "Unable to write backup file for " + 1827 destination.canonicalPath(); 1828 } 1829 } else { 1830 Log.error("writeFile failed"); 1831 message = "Unable to create temporary file in " + 1832 tempFile.getParent(); 1833 } 1834 } 1835 if (succeeded) { 1836 if (isTaggable()) 1837 Editor.getTagFileManager().addToQueue(destination.getParentFile(), mode); 1838 } else { 1839 if (message != null) 1841 MessageDialog.showMessageDialog(message, "Save Copy"); 1842 MessageDialog.showMessageDialog("Save failed", "Save Copy"); 1843 } 1844 } 1845 1846 private void saveCopyFtp(FtpFile destination) 1847 { 1848 Debug.assertTrue(SwingUtilities.isEventDispatchThread()); 1849 FtpSession session = FtpSession.getSession(destination); 1850 if (session == null) 1851 return; 1852 Debug.assertTrue(session.isLocked()); 1853 if (!lock()) { 1854 session.unlock(); 1855 MessageDialog.showMessageDialog("Buffer is busy", getFile().netPath()); 1856 return; 1857 } 1858 final Editor editor = Editor.currentEditor(); 1859 editor.setWaitCursor(); 1860 if (!saveToCache()) { 1861 unlock(); 1862 session.unlock(); 1863 editor.setDefaultCursor(); 1864 String message = "Unable to write temporary file for " + destination.netPath(); 1865 MessageDialog.showMessageDialog(message, "Save Copy"); 1866 return; 1867 } 1868 final Runnable successRunnable = new Runnable () { 1869 public void run() 1870 { 1871 for (EditorIterator it = new EditorIterator(); it.hasNext();) { 1872 Editor ed = it.nextEditor(); 1873 if (ed.getBuffer() == Buffer.this) 1874 ed.setDefaultCursor(); 1875 } 1876 Sidebar.repaintBufferListInAllFrames(); 1877 } 1878 }; 1879 final FtpSaveProcess saveProcess = 1880 new FtpSaveProcess(this, cache, destination, session); 1881 saveProcess.setConfirmOverwrite(true); 1882 saveProcess.setTitle("Save Copy"); 1883 saveProcess.setSuccessRunnable(successRunnable); 1884 Debug.assertTrue(isLocked()); 1885 saveProcess.start(); 1886 } 1887 1888 public void removeTrailingWhitespace() 1890 { 1891 boolean bufferChanged = false; 1892 CompoundEdit compoundEdit = null; 1893 try { 1894 lockWrite(); 1895 } 1896 catch (InterruptedException e) { 1897 Log.error(e); 1898 return; 1899 } 1900 try { 1901 for (Line line = getFirstLine(); line != null; line = line.next()) { 1902 final String text = line.getText(); 1903 final int originalLength = text.length(); 1904 int length = originalLength; 1905 for (int i = originalLength - 1; i >= 0; i--) { 1906 char c = text.charAt(i); 1907 if (c == ' ' || c == '\t') 1908 --length; 1909 else 1910 break; 1911 } 1912 if (length != originalLength) { 1913 if (!bufferChanged) { 1914 compoundEdit = new CompoundEdit (); 1916 bufferChanged = true; 1917 } 1918 compoundEdit.addEdit(new UndoLineEdit(this, line)); 1919 line.setText(text.substring(0, length)); 1920 } 1921 } 1922 if (bufferChanged) 1923 modified(); 1924 } 1925 finally { 1926 unlockWrite(); 1927 } 1928 if (compoundEdit != null && undoManager != null) { 1929 compoundEdit.end(); 1930 undoManager.addEdit(compoundEdit); 1931 } 1932 if (bufferChanged) { 1933 for (EditorIterator it = new EditorIterator(); it.hasNext();) { 1934 Editor ed = it.nextEditor(); 1935 if (ed.getBuffer() == this) { 1936 if (ed.getDotOffset() > ed.getDotLine().length()) { 1937 ed.getDot().setOffset(ed.getDotLine().length()); 1938 if (getBooleanProperty(Property.RESTRICT_CARET)) { 1939 ed.moveCaretToDotCol(); 1940 ed.updateDotLine(); 1941 } 1942 } 1943 if (ed.getMark() != null) { 1944 if (ed.getMarkOffset() > ed.getMarkLine().length()) 1945 ed.getMark().setOffset(ed.getMarkLine().length()); 1946 } 1947 ed.repaintDisplay(); 1948 } 1949 } 1950 } 1951 } 1952 1953 public synchronized void autosave() 1954 { 1955 if (autosaveEnabled && Autosave.isAutosaveEnabled()) 1956 if (modCount != autosaveModCount) 1957 new Thread (autosaveRunnable, "autosave").start(); 1958 } 1959 1960 private final Runnable autosaveRunnable = new Runnable () { 1961 public void run() 1962 { 1963 try { 1964 lockRead(); 1965 } 1966 catch (InterruptedException e) { 1967 Log.error(e); 1968 return; 1969 } 1970 try { 1971 autosaveInternal(); 1972 } 1973 finally { 1974 unlockRead(); 1975 } 1976 } 1977 }; 1978 1979 private void autosaveInternal() 1980 { 1981 if (autosaveFile == null) { 1982 final File autosaveDirectory = Autosave.getAutosaveDirectory(); 1983 if (autosaveDirectory == null) 1984 return; 1985 autosaveFile = Utilities.getTempFile(autosaveDirectory); 1986 if (autosaveFile == null) 1987 return; 1988 Autosave.put(getFile().netPath(), autosaveFile.getName()); 1990 Autosave.flush(); 1991 } 1992 autosaveFile.setEncoding(getFile().getEncoding()); 1993 if (writeFile(autosaveFile)) 1994 autosaveModCount = modCount; 1995 else 1996 Log.error("autosave writeFile failed"); 1997 } 1998 1999 public void deleteAutosaveFile() 2000 { 2001 if (autosaveFile != null) 2002 autosaveFile.delete(); 2003 } 2004 2005 public void setFirstLine(Line line) 2006 { 2007 if (!rwlock.isWriteLocked()) { 2008 Log.error("----- setFirstLine() called without write lock -----"); 2009 Debug.dumpStack(); 2010 } 2011 super.setFirstLine(line); 2012 } 2013 2014 public void modified() 2015 { 2016 if (!rwlock.isWriteLocked()) { 2017 Log.error("----- modified() called without write lock -----"); 2018 Debug.dumpStack(); 2019 } 2020 if (modCount > saveModCount) { 2021 incrementModCount(); 2023 } else { 2024 setModCount(saveModCount + 1); 2026 Sidebar.setUpdateFlagInAllFrames(SIDEBAR_MODIFIED_BUFFER_COUNT); 2027 Sidebar.repaintBufferListInAllFrames(); 2028 } 2029 invalidate(); 2030 } 2031 2032 public void unmodified() 2033 { 2034 setModCount(0); 2035 saveModCount = 0; 2036 autosaveModCount = 0; 2037 Sidebar.setUpdateFlagInAllFrames(SIDEBAR_MODIFIED_BUFFER_COUNT); 2038 Sidebar.repaintBufferListInAllFrames(); 2039 } 2040 2041 public void saved() 2042 { 2043 saveModCount = modCount; 2044 autosaveModCount = modCount; 2045 if (isNewFile()) { 2046 for (Line line = getFirstLine(); line != null; line = line.next()) { 2047 line.setOriginalText(null); 2048 line.setNew(false); 2049 } 2050 setNewFile(false); 2051 } else { 2052 for (Line line = getFirstLine(); line != null; line = line.next()) { 2053 if (line.isModified()) 2054 line.setSaved(true); 2055 } 2056 if (getBooleanProperty(Property.SHOW_CHANGE_MARKS)) 2057 repaint(); 2058 } 2059 backedUp = false; Sidebar.setUpdateFlagInAllFrames(SIDEBAR_MODIFIED_BUFFER_COUNT); 2061 Sidebar.repaintBufferListInAllFrames(); 2062 deleteAutosaveFile(); 2063 } 2064 2065 public void empty() 2066 { 2067 try { 2068 lockWrite(); 2069 } 2070 catch (InterruptedException e) { 2071 Log.debug(e); 2072 return; 2073 } 2074 try { 2075 _empty(); 2076 } 2077 finally { 2078 unlockWrite(); 2079 } 2080 setTags(null); 2081 if (lastView != null) 2084 lastView.invalidate(); 2085 for (EditorIterator it = new EditorIterator(); it.hasNext();) { 2086 Editor ed = it.nextEditor(); 2087 View view = ed.getView(this); 2088 if (view != null) 2089 view.invalidate(); 2090 if (ed.getBuffer() == this) { 2091 ed.setDot(null); 2092 ed.setMark(null); 2093 ed.setTopLine(null); 2094 } 2095 } 2096 } 2097 2098 public void invalidate() 2099 { 2100 needsParsing = true; 2101 maxColsValid = false; 2102 setTags(null); 2103 } 2104 2105 public final View getLastView() 2106 { 2107 if (lastView != null) 2108 return (View) lastView.clone(); 2109 else 2110 return null; 2111 } 2112 2113 public final void setLastView(View view) 2114 { 2115 lastView = (View) view.clone(); 2116 } 2117 2118 public void saveView(Editor editor) 2119 { 2120 final View view = saveViewInternal(editor); 2121 editor.setView(this, view); 2122 setLastView(view); 2123 } 2124 2125 protected View saveViewInternal(Editor editor) 2126 { 2127 final Display display = editor.getDisplay(); 2128 View view = editor.getView(this); 2129 if (view == null) 2130 view = new View(); 2131 final Position dot = editor.getDot(); 2132 view.dot = dot == null ? null : new Position(dot); 2133 final Position mark = editor.getMark(); 2134 view.mark = mark == null ? null : new Position(mark); 2135 view.selection = editor.getSelection(); 2136 view.setColumnSelection(editor.isColumnSelection()); 2137 view.topLine = editor.getTopLine(); 2138 if (view.topLine != null) 2139 view.topLineNumber = view.topLine.lineNumber(); 2140 view.pixelsAboveTopLine = display.getPixelsAboveTopLine(); 2141 view.shift = display.shift; 2142 view.caretCol = display.caretCol; 2143 view.timestamp = System.currentTimeMillis(); 2144 if (view.dot == null) { 2145 view.lineNumber = 0; 2146 view.offs = 0; 2147 } else { 2148 view.lineNumber = view.dot.lineNumber(); 2149 view.offs = view.dot.getOffset(); 2150 } 2151 return view; 2152 } 2153 2154 public boolean needsParsing() 2155 { 2156 return needsParsing; 2157 } 2158 2159 public final void setNeedsParsing(boolean b) 2160 { 2161 needsParsing = b; 2162 } 2163 2164 public boolean isModified() 2165 { 2166 return modCount != saveModCount; 2167 } 2168 2169 public Line getLine(int lineNumber) 2170 { 2171 if (lineNumber < 0) 2172 return null; 2173 int n = 0; 2174 Line line = getFirstLine(); 2175 while (line != null && n != lineNumber) { 2176 line = line.next(); 2177 ++n; 2178 } 2179 return line; 2180 } 2181 2182 2190 public Position findOriginal(int lineNumber, int offset) 2191 { 2192 if (offset < 0) 2193 offset = 0; 2194 Line line = getFirstLine(); 2195 while (line != null && line.originalLineNumber() != lineNumber) 2196 line = line.next(); 2197 if (line != null) 2198 return new Position(line, Math.min(offset, line.length())); 2199 if (line == null) { 2200 line = getFirstLine(); 2203 while (line != null && line.originalLineNumber() < lineNumber) 2204 line = line.next(); 2205 } 2206 if (line != null) 2207 return new Position(line, 0); 2208 return getEnd(); 2209 } 2210 2211 public int getAbsoluteOffset(Position pos) 2213 { 2214 Line targetLine = pos.getLine(); 2215 int offset = 0; 2216 Line line = getFirstLine(); 2217 while (line != null && line != targetLine) { 2218 offset += line.length() + 1; 2219 line = line.next(); 2220 } 2221 if (line == null) 2222 return -1; else 2224 return offset + pos.getOffset(); 2225 } 2226 2227 public Position getPosition(int goal) 2229 { 2230 int offset = 0; 2231 Line line = getFirstLine(); 2232 while (line != null) { 2233 offset += line.length() + 1; 2235 if (offset >= goal) { 2236 if (offset == goal) 2237 line = line.next(); 2238 break; 2239 } 2240 line = line.next(); 2241 } 2242 if (line == null) 2243 return null; if (offset == goal) 2245 return new Position(line, 0); 2246 offset -= line.length() + 1; 2247 return new Position(line, goal - offset); 2248 } 2249 2250 private UndoManager undoManager; 2251 2252 protected void initializeUndo() 2253 { 2254 undoManager = new UndoManager(); 2255 } 2256 2257 public void resetUndo() 2258 { 2259 if (supportsUndo) 2260 undoManager.discardAllEdits(); 2261 } 2262 2263 public void resetRedo() 2264 { 2265 } 2266 2267 public final UndoManager getUndoManager() 2268 { 2269 return undoManager; 2270 } 2271 2272 public final void addEdit(UndoableEdit edit) 2273 { 2274 if (undoManager != null) 2275 undoManager.addEdit(edit); 2276 } 2277 2278 public final void addUndoBoundary() 2279 { 2280 if (undoManager != null) 2281 undoManager.addEdit(UndoBoundary.getInstance()); 2282 } 2283 2284 public final void appendUndoFold(Editor editor) 2285 { 2286 if (undoManager != null) 2287 undoManager.appendUndoFold(editor); 2288 } 2289 2290 public boolean canUndo() 2291 { 2292 return (undoManager != null && undoManager.canUndo()); 2293 } 2294 2295 public void undo() 2296 { 2297 if (undoManager != null) { 2298 if (needsRenumbering) 2299 renumber(); 2300 try { 2301 undoManager.undo(); 2302 } 2303 catch (CannotUndoException e) { 2304 Editor.currentEditor().status("Nothing to undo"); 2305 } 2306 } 2307 } 2308 2309 public boolean canRedo() 2310 { 2311 return (undoManager != null && undoManager.canRedo()); 2312 } 2313 2314 public void redo() 2315 { 2316 if (undoManager != null) { 2317 if (needsRenumbering) 2318 renumber(); 2319 try { 2320 undoManager.redo(); 2321 } 2322 catch (CannotRedoException e) { 2323 Editor.currentEditor().status("Nothing to redo"); 2324 } 2325 } 2326 } 2327 2328 public CompoundEdit beginCompoundEdit() 2329 { 2330 if (supportsUndo) { 2331 CompoundEdit compoundEdit = new CompoundEdit (); 2332 undoManager.addEdit(compoundEdit); 2333 return compoundEdit; 2334 } else 2335 return null; 2336 } 2337 2338 public void endCompoundEdit(CompoundEdit compoundEdit) 2339 { 2340 if (compoundEdit != null) 2341 compoundEdit.end(); 2342 } 2343 2344 protected void setText(String text) 2345 { 2346 try { 2347 lockWrite(); 2348 } 2349 catch (InterruptedException e) { 2350 Log.debug(e); 2351 return; 2352 } 2353 try { 2354 empty(); 2355 if (text != null) { 2356 FastStringReader reader = new FastStringReader(text); 2357 String s; 2358 while ((s = reader.readLine()) != null) 2359 appendLine(s); 2360 } 2361 if (getFirstLine() == null) 2362 appendLine(""); 2363 renumber(); 2364 invalidate(); 2365 setLoaded(true); 2366 } 2367 finally { 2368 unlockWrite(); 2369 } 2370 } 2371 2372 public void insertString(Position pos, String s) 2374 { 2375 try { 2376 lockWrite(); 2377 } 2378 catch (InterruptedException e) { 2379 Log.error(e); 2380 return; 2381 } 2382 try { 2383 FastStringBuffer sb = new FastStringBuffer(); 2384 final int limit = s.length(); 2385 boolean skipLF = false; 2386 for (int i = 0; i < limit; i++) { 2387 char c = s.charAt(i); 2388 if (c == '\r') { 2389 if (sb.length() > 0) { 2390 insertChars(pos, sb.toString()); 2391 sb.setLength(0); 2392 } 2393 insertLineSeparator(pos); 2394 skipLF = true; 2395 } else if (c == '\n') { 2396 if (skipLF) { 2397 skipLF = false; 2398 } else { 2399 if (sb.length() > 0) { 2400 insertChars(pos, sb.toString()); 2401 sb.setLength(0); 2402 } 2403 insertLineSeparator(pos); 2404 } 2405 } else { 2406 skipLF = false; 2407 sb.append(c); 2408 } 2409 } 2410 if (sb.length() > 0) 2411 insertChars(pos, sb.toString()); 2412 } 2413 finally { 2414 unlockWrite(); 2415 } 2416 } 2417 2418 public void insertChars(Position pos, String s) 2421 { 2422 final int length = s.length(); 2423 if (length > 0) { 2424 String text = pos.getLine().getText(); 2425 FastStringBuffer sb = new FastStringBuffer(text.length() + length); 2426 sb.append(text.substring(0, pos.getOffset())); 2427 sb.append(s); 2428 sb.append(text.substring(pos.getOffset())); 2429 pos.getLine().setText(sb.toString()); 2430 pos.skip(length); 2431 modified(); 2432 } 2433 } 2434 2435 public void insertLineSeparator(Position pos) 2436 { 2437 final Line line = pos.getLine(); 2438 final int offset = pos.getOffset(); 2439 if (offset == 0) { 2440 final Line newLine = new TextLine(""); 2441 newLine.setNew(true); 2442 newLine.setFlags(line.flags()); 2444 final Line prevLine = line.previous(); 2445 newLine.setPrevious(prevLine); 2446 if (prevLine != null) { 2447 prevLine.setNext(newLine); 2448 } else { 2449 Debug.assertTrue(line == getFirstLine()); 2450 setFirstLine(newLine); 2451 } 2452 newLine.setNext(line); 2453 line.setPrevious(newLine); 2454 } else if (offset == line.length()) { 2455 final Line newLine = new TextLine(""); 2456 newLine.setNew(true); 2457 newLine.setFlags(line.flags()); 2459 final Line prevLine = line; 2460 final Line nextLine = line.next(); 2461 newLine.setPrevious(prevLine); 2462 if (prevLine != null) { 2463 prevLine.setNext(newLine); 2464 } else { 2465 Debug.assertTrue(line == getFirstLine()); 2466 setFirstLine(newLine); 2467 } 2468 newLine.setNext(nextLine); 2469 if (nextLine != null) 2470 nextLine.setPrevious(newLine); 2471 pos.moveTo(newLine, 0); 2472 } else { 2473 final String head = line.substring(0, offset); 2474 final String tail = line.substring(offset); 2475 line.setText(head); 2476 final Line nextLine = line.next(); 2477 final Line newLine = new TextLine(tail); 2478 newLine.setNew(true); 2479 newLine.setFlags(line.flags()); 2481 line.setNext(newLine); 2482 newLine.setPrevious(line); 2483 newLine.setNext(nextLine); 2484 if (nextLine != null) 2485 nextLine.setPrevious(newLine); 2486 pos.moveTo(newLine, 0); 2487 } 2488 needsRenumbering = true; 2489 modified(); 2490 repaint(); 2491 } 2492 2493 public final void repaint() 2495 { 2496 for (EditorIterator it = new EditorIterator(); it.hasNext();) { 2497 Editor ed = it.nextEditor(); 2498 if (ed.getBuffer() == this) 2499 ed.repaintDisplay(); 2500 } 2501 } 2502 2503 public String getTitle() 2504 { 2505 if (title != null) 2506 return title; 2507 final File file = getFile(); 2508 if (file != null) 2509 return file.netPath(); 2510 else 2511 return ""; 2512 } 2513 2514 public final void setTitle(String s) 2515 { 2516 title = s; 2517 } 2518 2519 public String getFileNameForDisplay() 2520 { 2521 final File file = getFile(); 2522 if (file != null) 2523 return file.isRemote() ? file.netPath() : file.canonicalPath(); 2524 return ""; 2525 } 2526 2527 public String toString() 2529 { 2530 if (title != null) 2531 return title; 2532 final File file = getFile(); 2533 if (file == null) { 2534 return null; 2536 } 2537 if (file instanceof HttpFile) 2538 return file.netPath(); 2539 if (file.isRemote()) { 2540 FastStringBuffer sb = new FastStringBuffer(file.getName()); 2542 sb.append(" ["); 2543 sb.append(file.netPath()); 2544 sb.append(']'); 2545 return sb.toString(); 2546 } 2547 if (file.isDirectory()) 2549 return file.canonicalPath(); 2550 return Editor.getBufferList().getUniqueName(this); 2551 } 2552 2553 public Icon getIcon() 2555 { 2556 if (isModified()) 2557 return Utilities.getIconFromFile("modified.png"); 2558 else if (isReadOnly()) 2559 return Utilities.getIconFromFile("read_only.png"); 2560 else 2561 return Utilities.getIconFromFile("buffer.png"); 2562 } 2563 2564 public final int getCol(Position pos) 2565 { 2566 return getCol(pos.getLine(), pos.getOffset()); 2567 } 2568 2569 public final int getCol(Line line, int offset) 2572 { 2573 return getCol(line, offset, getTabWidth()); 2574 } 2575 2576 public final static int getCol(Line line, int offset, int tabWidth) 2577 { 2578 final int limit = Math.min(offset, line.length()); 2579 int col = 0; 2580 for (int i = 0; i < limit; i++) { 2581 if (line.charAt(i) == '\t') 2582 col += tabWidth - col % tabWidth; 2583 else 2584 ++col; 2585 } 2586 return col; 2587 } 2588 2589 public int getIndentation(Line line) 2591 { 2592 int indent = 0; 2593 final int tabWidth = getTabWidth(); 2594 final int limit = line.length(); 2595 for (int i = 0; i < limit; i++) { 2596 char c = line.charAt(i); 2597 if (c == '\t') 2598 indent += tabWidth - indent % tabWidth; 2599 else if (c == ' ') 2600 ++indent; 2601 else 2602 break; 2603 } 2604 return indent; 2605 } 2606 2607 public void setIndentation(Line line, int indent) 2609 { 2610 final int length = line.length(); 2612 int i = 0; 2613 while (i < length && Character.isWhitespace(line.charAt(i))) 2614 ++i; 2615 if (i < length) { 2616 String remainder = line.substring(i); 2617 if (indent > 0) { 2618 FastStringBuffer sb = getCorrectIndentationString(indent); 2620 sb.append(remainder); 2621 line.setText(sb.toString()); 2622 } else 2623 line.setText(remainder); 2624 } else 2625 line.setText(""); 2626 } 2627 2628 public FastStringBuffer getCorrectIndentationString(int indent) 2629 { 2630 FastStringBuffer sb = new FastStringBuffer(256); 2631 if (getUseTabs()) { 2632 final int tabWidth = getTabWidth(); 2633 int col = 0; 2634 while (col + tabWidth <= indent) { 2635 sb.append('\t'); 2636 col += tabWidth; 2637 } 2638 while (col < indent) { 2639 sb.append(' '); 2640 ++col; 2641 } 2642 } else 2643 sb.append(Utilities.spaces(indent)); 2644 return sb; 2645 } 2646 2647 private boolean folded; 2648 2649 public final void renumber() 2650 { 2651 folded = false; 2652 lineCount = 0; 2653 visibleLineCount = 0; 2654 for (Line line = getFirstLine(); line != null; line = line.next()) { 2655 line.setLineNumber(lineCount++); 2656 if (line.isHidden()) 2657 folded = true; 2658 else 2659 ++visibleLineCount; 2660 } 2661 needsRenumbering = false; 2662 } 2663 2664 protected void renumberOriginal() 2665 { 2666 folded = false; 2667 lineCount = 0; 2668 visibleLineCount = 0; 2669 for (Line line = getFirstLine(); line != null; line = line.next()) { 2670 line.setOriginalLineNumber(lineCount); 2671 line.setLineNumber(lineCount++); 2672 if (line.isHidden()) 2673 folded = true; 2674 else 2675 ++visibleLineCount; 2676 } 2677 needsRenumbering = false; 2678 } 2679 2680 protected void enforceOutputLimit(Property property) 2681 { 2682 Debug.assertTrue(property != null); 2683 final int outputLimit = 2684 Editor.preferences().getIntegerProperty(property); 2685 if (outputLimit == 0 || lineCount <= outputLimit) 2686 return; 2687 try { 2688 lockWrite(); 2689 } 2690 catch (InterruptedException e) { 2691 Log.error(e); 2692 return; 2693 } 2694 try { 2695 Position begin = new Position(getFirstLine(), 0); 2696 Line line = getFirstLine(); 2697 for (int i = lineCount - outputLimit; i > 0; i--) { 2698 if (line.next() == null) 2699 break; 2700 line = line.next(); 2701 } 2702 Position end = new Position(line, 0); 2703 Region r = new Region(this, begin, end); 2704 r.delete(); 2705 if (needsRenumbering) 2706 renumber(); 2707 resetUndo(); 2708 } 2709 finally { 2710 unlockWrite(); 2711 } 2712 } 2713 2714 public void saveProperties() 2715 { 2716 if (type != TYPE_NORMAL) 2717 return; 2718 if (isUntitled) 2719 return; 2720 final File file = getFile(); 2721 if (file == null) 2722 return; 2723 final String netPath = file.netPath(); 2724 if (netPath.indexOf('<') >= 0 || netPath.indexOf('&') >= 0) 2727 return; 2728 FileHistoryEntry entry = new FileHistoryEntry(); 2729 entry.setName(netPath); 2730 entry.setEncoding(file.getEncoding()); 2731 entry.setMode(mode.toString()); 2732 entry.setWhen(System.currentTimeMillis()); 2733 entry.setProperties(properties); 2734 FileHistory fileHistory = FileHistory.getFileHistory(); 2735 fileHistory.store(entry); 2736 fileHistory.save(); 2737 } 2738 2739 public void windowClosing() 2740 { 2741 } 2742 2743 public void dispose() 2744 { 2745 if (cache != null && cache.isFile()) { 2746 for (BufferIterator it = new BufferIterator(); it.hasNext();) { 2748 Buffer buf = it.nextBuffer(); 2749 if (buf != this && buf.getCache() == cache) 2750 return; 2751 } 2752 cache.delete(); 2754 } 2755 } 2756 2757 protected void finalize() throws Throwable 2758 { 2759 try { 2760 lockWrite(); 2761 } 2762 catch (InterruptedException e) { 2763 Log.debug(e); 2764 return; 2765 } 2766 try { 2767 empty(); 2768 } 2769 finally { 2770 unlockWrite(); 2771 } 2772 super.finalize(); 2773 } 2774 2775 public final boolean canBeRestored() 2776 { 2777 final File file = getFile(); 2778 if (file != null && file.isRemote()) 2779 return false; 2780 if (this instanceof RemoteBuffer) 2781 return false; 2782 switch (type) { 2783 case TYPE_NORMAL: 2784 case TYPE_ARCHIVE: 2785 case TYPE_DIRECTORY: 2786 return true; 2787 default: 2788 return false; 2789 } 2790 } 2791 2792 public final boolean isUntitled() 2793 { 2794 return isUntitled; 2795 } 2796 2797 private boolean isTransient; 2798 2799 public boolean isTransient() 2800 { 2801 return isTransient; 2802 } 2803 2804 public final void setTransient(boolean b) 2805 { 2806 unsplitOnClose = isTransient = b; 2807 } 2808 2809 private boolean unsplitOnClose; 2810 2811 public boolean unsplitOnClose() 2812 { 2813 return unsplitOnClose; 2814 } 2815 2816 public final void setUnsplitOnClose(boolean b) 2817 { 2818 unsplitOnClose = b; 2819 } 2820 2821 private SoftReference srText; 2823 2824 public synchronized String getText() 2826 { 2827 if (srText != null) { 2828 final String text = (String ) srText.get(); 2829 if (text != null) 2830 return text; 2831 } 2832 FastStringBuffer sb = new FastStringBuffer(16384); 2833 Line line = getFirstLine(); 2834 if (line != null) { 2835 String text = line.getText(); 2836 if (text != null) 2837 sb.append(text); 2838 line = line.next(); 2839 while (line != null) { 2840 text = line.getText(); 2841 if (text != null) { 2842 sb.append('\n'); 2843 sb.append(text); 2844 } 2845 line = line.next(); 2846 } 2847 } 2848 final String text = sb.toString(); 2849 srText = new SoftReference (text); 2850 return text; 2851 } 2852 2853 public boolean isEmpty() 2854 { 2855 Line first = getFirstLine(); 2856 if (first == null) 2857 return true; 2858 if (first.next() != null) 2859 return false; 2860 return first.length() == 0; 2861 } 2862 2863 public int getDisplayHeight() 2864 { 2865 return visibleLineCount * Display.getCharHeight(); 2866 } 2867 2868 public int getY(Line line) 2870 { 2871 Debug.assertTrue(!line.isHidden()); 2872 if (folded) { 2873 final int charHeight = Display.getCharHeight(); 2874 int y = 0; 2875 for (Line l = getFirstLine(); l != null; l = l.nextVisible()) { 2876 if (l == line) 2877 break; 2878 else 2879 y += charHeight; 2880 } 2881 return y; 2882 } else 2883 return line.lineNumber() * Display.getCharHeight(); 2884 } 2885 2886 public int getDisplayWidth() 2887 { 2888 return Display.getGutterWidth(this) + 2889 (getMaximumColumns()+1) * Display.getCharWidth(); 2890 } 2891 2892 private int maxCols = 0; 2893 private boolean maxColsValid = false; 2894 2895 public int getMaximumColumns() 2896 { 2897 if (maxCols == 0) { 2898 maxCols = calculateMaximumColumns(); 2899 maxColsValid = true; 2900 } 2901 return maxCols; 2902 } 2903 2904 private int calculateMaximumColumns() 2905 { 2906 if (getModeId() == BINARY_MODE) 2907 return getFirstLine().length(); 2908 final int tabWidth = getTabWidth(); 2909 int max = 0; 2910 for (Line line = getFirstLine(); line != null; line = line.next()) { 2911 int cols; 2912 if ((cols = getCol(line, line.length(), tabWidth)) > max) 2913 max = cols; 2914 } 2915 return max; 2916 } 2917 2918 public boolean validateMaximumColumns() 2920 { 2921 if (maxColsValid) 2922 return false; 2923 final int oldMaxCols = maxCols; 2924 maxCols = calculateMaximumColumns(); 2925 maxColsValid = true; 2926 return maxCols != oldMaxCols; 2927 } 2928 2929 public void setProperty(Property property, Object value) 2930 { 2931 properties.setProperty(property, value); 2932 } 2933 2934 public void setProperty(Property property, boolean value) 2935 { 2936 properties.setProperty(property, value); 2937 } 2938 2939 public void setProperty(Property property, int value) 2940 { 2941 properties.setProperty(property, value); 2942 } 2943 2944 boolean setPropertyFromString(Property property, String value) 2945 { 2946 return properties.setPropertyFromString(property, value); 2947 } 2948 2949 boolean removeProperty(Property property) 2950 { 2951 return properties.removeProperty(property); 2952 } 2953 2954 public String getStringProperty(Property property) 2955 { 2956 Object value = properties.getProperty(property); 2957 if (value instanceof String ) 2958 return (String ) value; 2959 if (mode != null) 2960 return mode.getStringProperty(property); 2961 return (String ) property.getDefaultValue(); 2962 } 2963 2964 public int getIntegerProperty(Property property) 2965 { 2966 if (!property.isIntegerProperty()) 2967 Debug.bug(); 2968 Object value = properties.getProperty(property); 2969 if (value instanceof Integer ) 2970 return ((Integer )value).intValue(); 2971 if (mode != null) 2972 return mode.getIntegerProperty(property); 2973 return ((Integer )property.getDefaultValue()).intValue(); 2974 } 2975 2976 public boolean getBooleanProperty(Property property) 2977 { 2978 if (!property.isBooleanProperty()) 2979 Debug.bug(); 2980 Object value = properties.getProperty(property); 2981 if (value == Boolean.TRUE) 2982 return true; 2983 if (value == Boolean.FALSE) 2984 return false; 2985 if (mode != null) 2986 return mode.getBooleanProperty(property); 2987 return ((Boolean )property.getDefaultValue()).booleanValue(); 2988 } 2989 2990 public final int getTabWidth() 2991 { 2992 return getIntegerProperty(Property.TAB_WIDTH); 2993 } 2994 2995 public void setTabWidth(int tabWidth) 2996 { 2997 properties.setProperty(Property.TAB_WIDTH, tabWidth); 2998 } 2999 3000 public final boolean getUseTabs() 3001 { 3002 return getBooleanProperty(Property.USE_TABS); 3003 } 3004 3005 public final int getIndentSize() 3006 { 3007 return getIntegerProperty(Property.INDENT_SIZE); 3008 } 3009 3010 public final void setIndentSize(int indentSize) 3011 { 3012 properties.setProperty(Property.INDENT_SIZE, indentSize); 3013 } 3014 3015 private static final Cursor textCursor = 3016 Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR); 3017 3018 public Cursor getDefaultCursor() 3019 { 3020 return textCursor; 3021 } 3022 3023 public Cursor getDefaultCursor(Position pos) 3024 { 3025 if (pos == null || pos.getLine() instanceof ImageLine) 3026 return Cursor.getDefaultCursor(); 3027 else 3028 return textCursor; 3029 } 3030 3031 public String getStatusText(Editor editor) 3032 { 3033 Debug.assertTrue(editor.getBuffer() == this); 3034 Position dot = editor.getDotCopy(); 3035 if (dot == null) 3036 return null; 3037 final FastStringBuffer sb = new FastStringBuffer(); 3038 if (cvsEntry != null) { 3039 sb.append("CVS "); 3040 final String revision = cvsEntry.getRevision(); 3041 if (revision.equals("0")) { 3042 sb.append('A'); 3043 } else { 3044 sb.append(revision); 3045 if (cvsEntry.getCheckoutTime() != lastModified) 3046 sb.append(" M"); 3047 } 3048 sb.append(" "); 3049 } 3050 if (Editor.preferences().getBooleanProperty(Property.STATUS_BAR_DISPLAY_LINE_SEPARATOR)) { 3051 if (lineSeparator != null) { 3052 if (lineSeparator.equals("\n")) 3053 sb.append("LF "); 3054 else if (lineSeparator.equals("\r\n")) 3055 sb.append("CR+LF "); 3056 else if (lineSeparator.equals("\r")) 3057 sb.append("CR "); 3058 } 3059 } 3060 sb.append("Line "); 3061 sb.append(String.valueOf(dot.lineNumber()+1)); 3062 if (Editor.preferences().getBooleanProperty(Property.STATUS_BAR_DISPLAY_LINE_COUNT)) { 3063 sb.append(" of "); 3064 sb.append(String.valueOf(getLineCount())); 3065 } 3066 sb.append(" Col "); 3067 final Display display = editor.getDisplay(); 3068 sb.append(String.valueOf(display.getAbsoluteCaretCol()+1)); 3069 if (getBooleanProperty(Property.WRAP)) 3070 sb.append(" Wrap"); 3071 if (Editor.isRecordingMacro()) 3072 sb.append(" [Recording macro...]"); 3073 return sb.toString(); 3074 } 3075 3076 public Expansion getExpansion(Position dot) 3077 { 3078 return new Expansion(dot, mode); 3079 } 3080 3081 public void restoreView(Editor editor) 3082 { 3083 final Display display = editor.getDisplay(); 3084 final View view = editor.getView(this); 3085 Debug.assertTrue(view != null); 3086 if (view != null) { 3087 if (view.getDot() == null) { 3088 Line line = getLine(view.getDotLineNumber()); 3089 if (line != null) { 3090 int offset = view.getDotOffset(); 3091 if (offset < 0) 3092 offset = 0; 3093 else if (offset > line.length()) 3094 offset = line.length(); 3095 view.setDot(new Position(line, offset)); 3096 } else { 3097 line = getFirstLine(); 3098 if (line != null) 3099 view.setDot(new Position(line, 0)); 3100 } 3101 } 3102 editor.setDot(view.getDot() == null ? null : new Position(view.getDot())); 3103 editor.setMark(view.getMark() == null ? null : new Position(view.getMark())); 3104 editor.setSelection(view.getSelection()); 3105 editor.setColumnSelection(view.isColumnSelection()); 3106 if (view.getTopLine() == null){ 3107 view.topLine = getFirstLine(); 3108 view.pixelsAboveTopLine = 0; 3109 } 3110 display.setTopLine(view.getTopLine()); 3111 display.setPixelsAboveTopLine(view.pixelsAboveTopLine); 3112 display.setShift(view.getShift()); 3113 display.setCaretCol(view.getCaretCol()); 3114 } 3115 } 3116 3117 private CVSEntry cvsEntry; 3118 3119 public final CVSEntry getCVSEntry() 3120 { 3121 return cvsEntry; 3122 } 3123 3124 public final void checkCVS() 3125 { 3126 cvsEntry = CVSEntry.parseEntryForFile(getFile()); 3127 } 3128 3129 public final boolean isKeyword(String s) 3130 { 3131 return mode.isKeyword(s); 3132 } 3133} 3134 | Popular Tags |