0001: /*
0002: * Buffer.java - jEdit buffer
0003: * :tabSize=8:indentSize=8:noTabs=false:
0004: * :folding=explicit:collapseFolds=1:
0005: *
0006: * Copyright (C) 1998, 2005 Slava Pestov
0007: * Portions copyright (C) 1999, 2000 mike dillon
0008: *
0009: * This program is free software; you can redistribute it and/or
0010: * modify it under the terms of the GNU General Public License
0011: * as published by the Free Software Foundation; either version 2
0012: * of the License, or any later version.
0013: *
0014: * This program is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0017: * GNU General Public License for more details.
0018: *
0019: * You should have received a copy of the GNU General Public License
0020: * along with this program; if not, write to the Free Software
0021: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0022: */
0023:
0024: package org.gjt.sp.jedit;
0025:
0026: //{{{ Imports
0027:
0028: import org.gjt.sp.jedit.browser.VFSBrowser;
0029: import org.gjt.sp.jedit.buffer.*;
0030: import org.gjt.sp.jedit.bufferio.BufferAutosaveRequest;
0031: import org.gjt.sp.jedit.bufferio.BufferIORequest;
0032: import org.gjt.sp.jedit.bufferio.MarkersSaveRequest;
0033: import org.gjt.sp.jedit.gui.StyleEditor;
0034: import org.gjt.sp.jedit.io.FileVFS;
0035: import org.gjt.sp.jedit.io.VFS;
0036: import org.gjt.sp.jedit.io.VFSFile;
0037: import org.gjt.sp.jedit.io.VFSManager;
0038: import org.gjt.sp.jedit.msg.BufferUpdate;
0039: import org.gjt.sp.jedit.syntax.*;
0040: import org.gjt.sp.jedit.textarea.JEditTextArea;
0041: import org.gjt.sp.jedit.textarea.Selection;
0042: import org.gjt.sp.util.IntegerArray;
0043: import org.gjt.sp.util.Log;
0044:
0045: import javax.swing.*;
0046: import javax.swing.text.AttributeSet;
0047: import javax.swing.text.Segment;
0048: import java.io.File;
0049: import java.io.IOException;
0050: import java.net.Socket;
0051: import java.util.Enumeration;
0052: import java.util.Hashtable;
0053: import java.util.Vector;
0054:
0055: //}}}
0056:
0057: /**
0058: * A <code>Buffer</code> represents the contents of an open text
0059: * file as it is maintained in the computer's memory (as opposed to
0060: * how it may be stored on a disk).<p>
0061: *
0062: * In a BeanShell script, you can obtain the current buffer instance from the
0063: * <code>buffer</code> variable.<p>
0064: *
0065: * This class does not have a public constructor.
0066: * Buffers can be opened and closed using methods in the <code>jEdit</code>
0067: * class.<p>
0068: *
0069: * This class is partially thread-safe, however you must pay attention to two
0070: * very important guidelines:
0071: * <ul>
0072: * <li>Changes to a buffer can only be made from the AWT thread.
0073: * <li>When accessing the buffer from another thread, you must
0074: * grab a read lock if you plan on performing more than one call, to ensure that
0075: * the buffer contents are not changed by the AWT thread for the duration of the
0076: * lock. Only methods whose descriptions specify thread safety can be invoked
0077: * from other threads.
0078: * </ul>
0079: *
0080: * @author Slava Pestov
0081: * @version $Id: Buffer.java 11199 2007-12-06 18:39:23Z k_satoda $
0082: */
0083: public class Buffer extends JEditBuffer {
0084: //{{{ Some constants
0085: /**
0086: * Backed up property.
0087: * @since jEdit 3.2pre2
0088: */
0089: public static final String BACKED_UP = "Buffer__backedUp";
0090:
0091: /**
0092: * Caret info properties.
0093: * @since jEdit 3.2pre1
0094: */
0095: public static final String CARET = "Buffer__caret";
0096: public static final String CARET_POSITIONED = "Buffer__caretPositioned";
0097:
0098: /**
0099: * Stores a List of {@link Selection} instances.
0100: */
0101: public static final String SELECTION = "Buffer__selection";
0102:
0103: /**
0104: * This should be a physical line number, so that the scroll
0105: * position is preserved correctly across reloads (which will
0106: * affect virtual line numbers, due to fold being reset)
0107: */
0108: public static final String SCROLL_VERT = "Buffer__scrollVert";
0109: public static final String SCROLL_HORIZ = "Buffer__scrollHoriz";
0110:
0111: /**
0112: * Should jEdit try to set the encoding based on a UTF8, UTF16 or
0113: * XML signature at the beginning of the file?
0114: */
0115: public static final String ENCODING_AUTODETECT = "encodingAutodetect";
0116:
0117: /**
0118: * This property is set to 'true' if the file has a trailing newline.
0119: * @since jEdit 4.0pre1
0120: */
0121: public static final String TRAILING_EOL = "trailingEOL";
0122:
0123: /**
0124: * This property is set to 'true' if the file should be GZipped.
0125: * @since jEdit 4.0pre4
0126: */
0127: public static final String GZIPPED = "gzipped";
0128:
0129: //}}}
0130:
0131: //{{{ Input/output methods
0132:
0133: //{{{ reload() method
0134: /**
0135: * Reloads the buffer from disk, asking for confirmation if the buffer
0136: * has unsaved changes.
0137: * @param view The view
0138: * @since jEdit 2.7pre2
0139: */
0140: public void reload(View view) {
0141: if (getFlag(UNTITLED))
0142: return;
0143: if (isDirty()) {
0144: String[] args = { path };
0145: int result = GUIUtilities.confirm(view, "changedreload",
0146: args, JOptionPane.YES_NO_OPTION,
0147: JOptionPane.WARNING_MESSAGE);
0148: if (result != JOptionPane.YES_OPTION)
0149: return;
0150: }
0151: EditPane[] editPanes = view.getEditPanes();
0152: for (int i = 0; i < editPanes.length; i++)
0153: editPanes[i].saveCaretInfo();
0154: load(view, true);
0155: } //}}}
0156:
0157: //{{{ load() method
0158: /**
0159: * Loads the buffer from disk.
0160: * @param view The view
0161: * @param reload If true, user will not be asked to recover autosave
0162: * file, if any
0163: *
0164: * @since 2.5pre1
0165: */
0166: public boolean load(final View view, final boolean reload) {
0167: if (isPerformingIO()) {
0168: GUIUtilities.error(view, "buffer-multiple-io", null);
0169: return false;
0170: }
0171:
0172: setBooleanProperty(BufferIORequest.ERROR_OCCURRED, false);
0173:
0174: setLoading(true);
0175:
0176: // view text areas temporarily blank out while a buffer is
0177: // being loaded, to indicate to the user that there is no
0178: // data available yet.
0179: if (!getFlag(TEMPORARY))
0180: EditBus.send(new BufferUpdate(this , view,
0181: BufferUpdate.LOAD_STARTED));
0182:
0183: final boolean loadAutosave;
0184:
0185: if (reload || !getFlag(NEW_FILE)) {
0186: if (file != null)
0187: modTime = file.lastModified();
0188:
0189: // Only on initial load
0190: if (!reload && autosaveFile != null
0191: && autosaveFile.exists())
0192: loadAutosave = recoverAutosave(view);
0193: else {
0194: if (autosaveFile != null)
0195: autosaveFile.delete();
0196: loadAutosave = false;
0197: }
0198:
0199: if (!loadAutosave) {
0200: VFS vfs = VFSManager.getVFSForPath(path);
0201:
0202: if (!checkFileForLoad(view, vfs, path)) {
0203: setLoading(false);
0204: return false;
0205: }
0206:
0207: // have to check again since above might set
0208: // NEW_FILE flag
0209: if (reload || !getFlag(NEW_FILE)) {
0210: if (!vfs.load(view, this , path)) {
0211: setLoading(false);
0212: return false;
0213: }
0214: }
0215: }
0216: } else
0217: loadAutosave = false;
0218:
0219: //{{{ Do some stuff once loading is finished
0220: Runnable runnable = new Runnable() {
0221: public void run() {
0222: String newPath = getStringProperty(BufferIORequest.NEW_PATH);
0223: Segment seg = (Segment) getProperty(BufferIORequest.LOAD_DATA);
0224: IntegerArray endOffsets = (IntegerArray) getProperty(BufferIORequest.END_OFFSETS);
0225:
0226: loadText(seg, endOffsets);
0227:
0228: unsetProperty(BufferIORequest.LOAD_DATA);
0229: unsetProperty(BufferIORequest.END_OFFSETS);
0230: unsetProperty(BufferIORequest.NEW_PATH);
0231:
0232: undoMgr.clear();
0233: undoMgr.setLimit(jEdit.getIntegerProperty(
0234: "buffer.undoCount", 100));
0235:
0236: if (!getFlag(TEMPORARY))
0237: finishLoading();
0238:
0239: setLoading(false);
0240:
0241: // if reloading a file, clear dirty flag
0242: if (reload)
0243: setDirty(false);
0244:
0245: if (!loadAutosave && newPath != null)
0246: setPath(newPath);
0247:
0248: // if loadAutosave is false, we loaded an
0249: // autosave file, so we set 'dirty' to true
0250:
0251: // note that we don't use setDirty(),
0252: // because a) that would send an unnecessary
0253: // message, b) it would also set the
0254: // AUTOSAVE_DIRTY flag, which will make
0255: // the autosave thread write out a
0256: // redundant autosave file
0257: if (loadAutosave)
0258: Buffer.super .setDirty(true);
0259:
0260: // send some EditBus messages
0261: if (!getFlag(TEMPORARY)) {
0262: fireBufferLoaded();
0263: EditBus.send(new BufferUpdate(Buffer.this , view,
0264: BufferUpdate.LOADED));
0265: //EditBus.send(new BufferUpdate(Buffer.this,
0266: // view,BufferUpdate.MARKERS_CHANGED));
0267: }
0268: }
0269: }; //}}}
0270:
0271: if (getFlag(TEMPORARY))
0272: runnable.run();
0273: else
0274: VFSManager.runInAWTThread(runnable);
0275:
0276: return true;
0277: } //}}}
0278:
0279: //{{{ insertFile() method
0280: /**
0281: * Loads a file from disk, and inserts it into this buffer.
0282: * @param view The view
0283: * @param path the path of the file to insert
0284: *
0285: * @since 4.0pre1
0286: */
0287: public boolean insertFile(View view, String path) {
0288: if (isPerformingIO()) {
0289: GUIUtilities.error(view, "buffer-multiple-io", null);
0290: return false;
0291: }
0292:
0293: setBooleanProperty(BufferIORequest.ERROR_OCCURRED, false);
0294:
0295: path = MiscUtilities.constructPath(this .path, path);
0296:
0297: Buffer buffer = jEdit.getBuffer(path);
0298: if (buffer != null) {
0299: view.getTextArea().setSelectedText(
0300: buffer.getText(0, buffer.getLength()));
0301: return true;
0302: }
0303:
0304: VFS vfs = VFSManager.getVFSForPath(path);
0305:
0306: // this returns false if initial sanity
0307: // checks (if the file is a directory, etc)
0308: // fail
0309: return vfs.insert(view, this , path);
0310: } //}}}
0311:
0312: //{{{ autosave() method
0313: /**
0314: * Autosaves this buffer.
0315: */
0316: public void autosave() {
0317: if (autosaveFile == null || !getFlag(AUTOSAVE_DIRTY)
0318: || !isDirty() || isPerformingIO())
0319: return;
0320:
0321: setFlag(AUTOSAVE_DIRTY, false);
0322:
0323: VFSManager.runInWorkThread(new BufferAutosaveRequest(null,
0324: this , null, VFSManager.getFileVFS(), autosaveFile
0325: .getPath()));
0326: } //}}}
0327:
0328: //{{{ saveAs() method
0329: /**
0330: * Prompts the user for a file to save this buffer to.
0331: * @param view The view
0332: * @param rename True if the buffer's path should be changed, false
0333: * if only a copy should be saved to the specified filename
0334: * @since jEdit 2.6pre5
0335: */
0336: public boolean saveAs(View view, boolean rename) {
0337: String[] files = GUIUtilities.showVFSFileDialog(view, path,
0338: VFSBrowser.SAVE_DIALOG, false);
0339:
0340: // files[] should have length 1, since the dialog type is
0341: // SAVE_DIALOG
0342: if (files == null)
0343: return false;
0344:
0345: return save(view, files[0], rename);
0346: } //}}}
0347:
0348: //{{{ save() method
0349: /**
0350: * Saves this buffer to the specified path name, or the current path
0351: * name if it's null.
0352: * @param view The view
0353: * @param path The path name to save the buffer to, or null to use
0354: * the existing path
0355: */
0356: public boolean save(View view, String path) {
0357: return save(view, path, true, false);
0358: } //}}}
0359:
0360: //{{{ save() method
0361: /**
0362: * Saves this buffer to the specified path name, or the current path
0363: * name if it's null.
0364: * @param view The view
0365: * @param path The path name to save the buffer to, or null to use
0366: * the existing path
0367: * @param rename True if the buffer's path should be changed, false
0368: * if only a copy should be saved to the specified filename
0369: * @since jEdit 2.6pre5
0370: */
0371: public boolean save(final View view, String path,
0372: final boolean rename) {
0373: return save(view, path, rename, false);
0374: } //}}}
0375:
0376: //{{{ save() method
0377: /**
0378: * Saves this buffer to the specified path name, or the current path
0379: * name if it's null.
0380: * @param view The view
0381: * @param path The path name to save the buffer to, or null to use
0382: * the existing path
0383: * @param rename True if the buffer's path should be changed, false
0384: * if only a copy should be saved to the specified filename
0385: * @param disableFileStatusCheck Disables file status checking
0386: * regardless of the state of the checkFileStatus property
0387: */
0388: public boolean save(final View view, String path,
0389: final boolean rename, boolean disableFileStatusCheck) {
0390: if (isPerformingIO()) {
0391: GUIUtilities.error(view, "buffer-multiple-io", null);
0392: return false;
0393: }
0394:
0395: setBooleanProperty(BufferIORequest.ERROR_OCCURRED, false);
0396:
0397: if (path == null && getFlag(NEW_FILE))
0398: return saveAs(view, rename);
0399:
0400: if (path == null && file != null) {
0401: long newModTime = file.lastModified();
0402:
0403: if (newModTime != modTime
0404: && jEdit.getBooleanProperty("view.checkModStatus")) {
0405: Object[] args = { this .path };
0406: int result = GUIUtilities.confirm(view,
0407: "filechanged-save", args,
0408: JOptionPane.YES_NO_OPTION,
0409: JOptionPane.WARNING_MESSAGE);
0410: if (result != JOptionPane.YES_OPTION)
0411: return false;
0412: }
0413: }
0414:
0415: EditBus.send(new BufferUpdate(this , view, BufferUpdate.SAVING));
0416:
0417: setPerformingIO(true);
0418:
0419: final String oldPath = this .path;
0420: final String oldSymlinkPath = symlinkPath;
0421: final String newPath = path == null ? this .path : path;
0422:
0423: VFS vfs = VFSManager.getVFSForPath(newPath);
0424:
0425: if (!checkFileForSave(view, vfs, newPath)) {
0426: setPerformingIO(false);
0427: return false;
0428: }
0429:
0430: Object session = vfs.createVFSSession(newPath, view);
0431: if (session == null) {
0432: setPerformingIO(false);
0433: return false;
0434: }
0435:
0436: unsetProperty("overwriteReadonly");
0437: unsetProperty("forbidTwoStageSave");
0438: try {
0439: VFSFile file = vfs._getFile(session, newPath, view);
0440: if (file != null) {
0441: boolean vfsRenameCap = (vfs.getCapabilities() & VFS.RENAME_CAP) != 0;
0442: if (!file.isWriteable()) {
0443: Log.log(Log.WARNING, this , "Buffer saving : File "
0444: + file + " is readOnly");
0445: if (vfsRenameCap) {
0446: Log.log(Log.DEBUG, this ,
0447: "Buffer saving : VFS can rename files");
0448: String savePath = vfs._canonPath(session,
0449: newPath, view);
0450: if (!MiscUtilities.isURL(savePath))
0451: savePath = MiscUtilities
0452: .resolveSymlinks(savePath);
0453: savePath = vfs.getTwoStageSaveName(savePath);
0454: if (savePath == null) {
0455: Log
0456: .log(Log.DEBUG, this ,
0457: "Buffer saving : two stage save impossible because path is null");
0458: VFSManager
0459: .error(
0460: view,
0461: newPath,
0462: "ioerror.save-readonly-twostagefail",
0463: null);
0464: setPerformingIO(false);
0465: return false;
0466: } else {
0467: int result = GUIUtilities.confirm(view,
0468: "vfs.overwrite-readonly",
0469: new Object[] { newPath },
0470: JOptionPane.YES_NO_OPTION,
0471: JOptionPane.WARNING_MESSAGE);
0472: if (result == JOptionPane.YES_OPTION) {
0473: Log
0474: .log(Log.WARNING, this ,
0475: "Buffer saving : two stage save will be used to save buffer");
0476: setBooleanProperty("overwriteReadonly",
0477: true);
0478: } else {
0479: Log.log(Log.DEBUG, this ,
0480: "Buffer not saved");
0481: setPerformingIO(false);
0482: return false;
0483: }
0484: }
0485: } else {
0486: Log
0487: .log(Log.WARNING, this ,
0488: "Buffer saving : file is readonly and vfs cannot do two stage save");
0489: VFSManager.error(view, newPath,
0490: "ioerror.write-error-readonly", null);
0491: setPerformingIO(false);
0492: return false;
0493: }
0494: } else {
0495: String savePath = vfs._canonPath(session, newPath,
0496: view);
0497: if (!MiscUtilities.isURL(savePath))
0498: savePath = MiscUtilities
0499: .resolveSymlinks(savePath);
0500: savePath = vfs.getTwoStageSaveName(savePath);
0501: if (jEdit.getBooleanProperty("twoStageSave")
0502: && (!vfsRenameCap || savePath == null)) {
0503: // the file is writeable but the vfs cannot do two stage. We must overwrite
0504: // readonly flag
0505:
0506: int result = GUIUtilities.confirm(view,
0507: "vfs.twostageimpossible",
0508: new Object[] { newPath },
0509: JOptionPane.YES_NO_OPTION,
0510: JOptionPane.WARNING_MESSAGE);
0511: if (result == JOptionPane.YES_OPTION) {
0512: Log
0513: .log(Log.WARNING, this ,
0514: "Buffer saving : two stage save cannot be used");
0515: setBooleanProperty("forbidTwoStageSave",
0516: true);
0517: } else {
0518: Log
0519: .log(Log.DEBUG, this ,
0520: "Buffer not saved");
0521: setPerformingIO(false);
0522: return false;
0523: }
0524:
0525: }
0526: }
0527: }
0528: } catch (IOException io) {
0529: VFSManager.error(view, newPath, "ioerror",
0530: new String[] { io.toString() });
0531: setPerformingIO(false);
0532: return false;
0533: } finally {
0534: try {
0535: vfs._endVFSSession(session, view);
0536: } catch (IOException io) {
0537: VFSManager.error(view, newPath, "ioerror",
0538: new String[] { io.toString() });
0539: setPerformingIO(false);
0540: return false;
0541: }
0542: }
0543:
0544: if (!vfs.save(view, this , newPath)) {
0545: setPerformingIO(false);
0546: return false;
0547: }
0548:
0549: // Once save is complete, do a few other things
0550: VFSManager.runInAWTThread(new Runnable() {
0551: public void run() {
0552: setPerformingIO(false);
0553: setProperty("overwriteReadonly", null);
0554: finishSaving(
0555: view,
0556: oldPath,
0557: oldSymlinkPath,
0558: newPath,
0559: rename,
0560: getBooleanProperty(BufferIORequest.ERROR_OCCURRED));
0561: updateMarkersFile(view);
0562: }
0563: });
0564:
0565: int check = jEdit.getIntegerProperty("checkFileStatus");
0566: if ((!disableFileStatusCheck) && (check == 1 || check == 2))
0567: jEdit.checkBufferStatus(view, false);
0568:
0569: return true;
0570: } //}}}
0571:
0572: //{{{ checkFileStatus() method
0573: public static final int FILE_NOT_CHANGED = 0;
0574: public static final int FILE_CHANGED = 1;
0575: public static final int FILE_DELETED = 2;
0576:
0577: /**
0578: * Check if the buffer has changed on disk.
0579: * @return One of <code>NOT_CHANGED</code>, <code>CHANGED</code>, or
0580: * <code>DELETED</code>.
0581: *
0582: * @since jEdit 4.2pre1
0583: */
0584: public int checkFileStatus(View view) {
0585: // - don't do these checks while a save is in progress,
0586: // because for a moment newModTime will be greater than
0587: // oldModTime, due to the multithreading
0588: // - only supported on local file system
0589: if (!isPerformingIO() && file != null && !getFlag(NEW_FILE)) {
0590: boolean newReadOnly = (file.exists() && !file.canWrite());
0591: if (newReadOnly != isFileReadOnly()) {
0592: setFileReadOnly(newReadOnly);
0593: EditBus.send(new BufferUpdate(this , null,
0594: BufferUpdate.DIRTY_CHANGED));
0595: }
0596:
0597: long oldModTime = modTime;
0598: long newModTime = file.lastModified();
0599:
0600: if (newModTime != oldModTime) {
0601: modTime = newModTime;
0602:
0603: if (!file.exists()) {
0604: setFlag(NEW_FILE, true);
0605: setDirty(true);
0606: return FILE_DELETED;
0607: } else {
0608: return FILE_CHANGED;
0609: }
0610: }
0611: }
0612:
0613: return FILE_NOT_CHANGED;
0614: } //}}}
0615:
0616: //}}}
0617:
0618: //{{{ Getters/setter methods for various buffer meta-data
0619:
0620: //{{{ getLastModified() method
0621: /**
0622: * Returns the last time jEdit modified the file on disk.
0623: * This method is thread-safe.
0624: */
0625: public long getLastModified() {
0626: return modTime;
0627: } //}}}
0628:
0629: //{{{ setLastModified() method
0630: /**
0631: * Sets the last time jEdit modified the file on disk.
0632: * @param modTime The new modification time
0633: */
0634: public void setLastModified(long modTime) {
0635: this .modTime = modTime;
0636: } //}}}
0637:
0638: //{{{ getAutoReload() method
0639: /**
0640: * Returns the status of the AUTORELOAD flag
0641: * If true, reload changed files automatically
0642: */
0643: public boolean getAutoReload() {
0644: return getFlag(AUTORELOAD);
0645: } //}}}
0646:
0647: //{{{ setAutoReload() method
0648: /**
0649: * Sets the status of the AUTORELOAD flag
0650: * @param value # If true, reload changed files automatically
0651: */
0652: public void setAutoReload(boolean value) {
0653: setFlag(AUTORELOAD, value);
0654: } //}}}
0655:
0656: //{{{ getAutoReloadDialog() method
0657: /**
0658: * Returns the status of the AUTORELOAD_DIALOG flag
0659: * If true, prompt for reloading or notify user
0660: * when the file has changed on disk
0661: */
0662: public boolean getAutoReloadDialog() {
0663: return getFlag(AUTORELOAD_DIALOG);
0664: } //}}}
0665:
0666: //{{{ setAutoReloadDialog() method
0667: /**
0668: * Sets the status of the AUTORELOAD_DIALOG flag
0669: * @param value # If true, prompt for reloading or notify user
0670: * when the file has changed on disk
0671:
0672: */
0673: public void setAutoReloadDialog(boolean value) {
0674: setFlag(AUTORELOAD_DIALOG, value);
0675: } //}}}
0676:
0677: //{{{ getVFS() method
0678: /**
0679: * Returns the virtual filesystem responsible for loading and
0680: * saving this buffer. This method is thread-safe.
0681: */
0682: public VFS getVFS() {
0683: return VFSManager.getVFSForPath(path);
0684: } //}}}
0685:
0686: //{{{ getAutosaveFile() method
0687: /**
0688: * Returns the autosave file for this buffer. This may be null if
0689: * the file is non-local.
0690: */
0691: public File getAutosaveFile() {
0692: return autosaveFile;
0693: } //}}}
0694:
0695: //{{{ removeAutosaveFile() method
0696: /**
0697: * Remove the autosave file.
0698: * @since jEdit 4.3pre12
0699: */
0700: public void removeAutosaveFile() {
0701: if (autosaveFile != null) {
0702: autosaveFile.delete();
0703: setFlag(AUTOSAVE_DIRTY, true);
0704: }
0705: } //}}}
0706:
0707: //{{{ getName() method
0708: /**
0709: * Returns the name of this buffer. This method is thread-safe.
0710: */
0711: public String getName() {
0712: return name;
0713: } //}}}
0714:
0715: //{{{ getPath() method
0716: /**
0717: * Returns the path name of this buffer. This method is thread-safe.
0718: */
0719: public String getPath() {
0720: return path;
0721: } //}}}
0722:
0723: //{{{ getSymlinkPath() method
0724: /**
0725: * If this file is a symbolic link, returns the link destination.
0726: * Otherwise returns the file's path. This method is thread-safe.
0727: * @since jEdit 4.2pre1
0728: */
0729: public String getSymlinkPath() {
0730: return symlinkPath;
0731: } //}}}
0732:
0733: //{{{ getDirectory() method
0734: /**
0735: * Returns the directory containing this buffer.
0736: * @since jEdit 4.1pre11
0737: */
0738: public String getDirectory() {
0739: return directory;
0740: } //}}}
0741:
0742: //{{{ isClosed() method
0743: /**
0744: * Returns true if this buffer has been closed with
0745: * {@link org.gjt.sp.jedit.jEdit#closeBuffer(View,Buffer)}.
0746: * This method is thread-safe.
0747: */
0748: public boolean isClosed() {
0749: return getFlag(CLOSED);
0750: } //}}}
0751:
0752: //{{{ isLoaded() method
0753: /**
0754: * Returns true if the buffer is loaded. This method is thread-safe.
0755: */
0756: public boolean isLoaded() {
0757: return !isLoading();
0758: } //}}}
0759:
0760: //{{{ isNewFile() method
0761: /**
0762: * Returns whether this buffer lacks a corresponding version on disk.
0763: * This method is thread-safe.
0764: */
0765: public boolean isNewFile() {
0766: return getFlag(NEW_FILE);
0767: } //}}}
0768:
0769: //{{{ setNewFile() method
0770: /**
0771: * Sets the new file flag.
0772: * @param newFile The new file flag
0773: */
0774: public void setNewFile(boolean newFile) {
0775: setFlag(NEW_FILE, newFile);
0776: if (!newFile)
0777: setFlag(UNTITLED, false);
0778: } //}}}
0779:
0780: //{{{ isUntitled() method
0781: /**
0782: * Returns true if this file is 'untitled'. This method is thread-safe.
0783: */
0784: public boolean isUntitled() {
0785: return getFlag(UNTITLED);
0786: } //}}}
0787:
0788: //{{{ setDirty() method
0789: /**
0790: * Sets the 'dirty' (changed since last save) flag of this buffer.
0791: */
0792: public void setDirty(boolean d) {
0793: boolean old_d = isDirty();
0794: super .setDirty(d);
0795: boolean editable = isEditable();
0796:
0797: if (d) {
0798: if (editable)
0799: setFlag(AUTOSAVE_DIRTY, true);
0800: } else {
0801: setFlag(AUTOSAVE_DIRTY, false);
0802:
0803: if (autosaveFile != null)
0804: autosaveFile.delete();
0805: }
0806:
0807: if (d != old_d && editable) {
0808: EditBus.send(new BufferUpdate(this , null,
0809: BufferUpdate.DIRTY_CHANGED));
0810: }
0811: } //}}}
0812:
0813: //{{{ isTemporary() method
0814: /**
0815: * Returns if this is a temporary buffer. This method is thread-safe.
0816: * @see jEdit#openTemporary(View,String,String,boolean)
0817: * @see jEdit#commitTemporary(Buffer)
0818: * @since jEdit 2.2pre7
0819: */
0820: public boolean isTemporary() {
0821: return getFlag(TEMPORARY);
0822: } //}}}
0823:
0824: //{{{ getIcon() method
0825: /**
0826: * Returns this buffer's icon.
0827: * @since jEdit 2.6pre6
0828: */
0829: public Icon getIcon() {
0830: if (isDirty())
0831: return GUIUtilities.loadIcon("dirty.gif");
0832: else if (isReadOnly())
0833: return GUIUtilities.loadIcon("readonly.gif");
0834: else if (getFlag(NEW_FILE))
0835: return GUIUtilities.loadIcon("new.gif");
0836: else
0837: return GUIUtilities.loadIcon("normal.gif");
0838: } //}}}
0839:
0840: //}}}
0841:
0842: //{{{ Buffer events
0843:
0844: //{{{ addBufferChangeListener() method
0845: /**
0846: * @deprecated Call {@link JEditBuffer#addBufferListener(BufferListener,int)}.
0847: */
0848: public void addBufferChangeListener(BufferChangeListener listener,
0849: int priority) {
0850: addBufferListener(new BufferChangeListener.Adapter(listener),
0851: priority);
0852: } //}}}
0853:
0854: //{{{ addBufferChangeListener() method
0855: /**
0856: * @deprecated Call {@link JEditBuffer#addBufferListener(BufferListener)}.
0857: */
0858: public void addBufferChangeListener(BufferChangeListener listener) {
0859: addBufferChangeListener(listener, NORMAL_PRIORITY);
0860: } //}}}
0861:
0862: //{{{ removeBufferChangeListener() method
0863: /**
0864: * @deprecated Call {@link JEditBuffer#removeBufferListener(BufferListener)}.
0865: */
0866: public void removeBufferChangeListener(BufferChangeListener listener) {
0867: BufferListener[] listeners = getBufferListeners();
0868:
0869: for (int i = 0; i < listeners.length; i++) {
0870: BufferListener l = listeners[i];
0871: if (l instanceof BufferChangeListener.Adapter) {
0872: if (((BufferChangeListener.Adapter) l).getDelegate() == listener) {
0873: removeBufferListener(l);
0874: return;
0875: }
0876: }
0877: }
0878: } //}}}
0879:
0880: //}}}
0881:
0882: //{{{ Property methods
0883:
0884: //{{{ propertiesChanged() method
0885: /**
0886: * Reloads settings from the properties. This should be called
0887: * after the <code>syntax</code> or <code>folding</code>
0888: * buffer-local properties are changed.
0889: */
0890: public void propertiesChanged() {
0891: super .propertiesChanged();
0892: EditBus.send(new BufferUpdate(this , null,
0893: BufferUpdate.PROPERTIES_CHANGED));
0894: } //}}}
0895:
0896: //{{{ getDefaultProperty() method
0897: public Object getDefaultProperty(String name) {
0898: Object retVal;
0899:
0900: if (mode != null) {
0901: retVal = mode.getProperty(name);
0902: if (retVal == null)
0903: return null;
0904:
0905: setDefaultProperty(name, retVal);
0906: return retVal;
0907: }
0908: // Now try buffer.<property>
0909: String value = jEdit.getProperty("buffer." + name);
0910: if (value == null)
0911: return null;
0912:
0913: // Try returning it as an integer first
0914: try {
0915: retVal = new Integer(value);
0916: } catch (NumberFormatException nf) {
0917: retVal = value;
0918: }
0919:
0920: return retVal;
0921: } //}}}
0922:
0923: //{{{ toggleWordWrap() method
0924: /**
0925: * Toggles word wrap between the three available modes. This is used
0926: * by the status bar.
0927: * @param view We show a message in the view's status bar
0928: * @since jEdit 4.1pre3
0929: */
0930: public void toggleWordWrap(View view) {
0931: String wrap = getStringProperty("wrap");
0932: if (wrap.equals("none"))
0933: wrap = "soft";
0934: else if (wrap.equals("soft"))
0935: wrap = "hard";
0936: else if (wrap.equals("hard"))
0937: wrap = "none";
0938: view.getStatus().setMessageAndClear(
0939: jEdit.getProperty("view.status.wrap-changed",
0940: new String[] { wrap }));
0941: setProperty("wrap", wrap);
0942: propertiesChanged();
0943: } //}}}
0944:
0945: //{{{ toggleLineSeparator() method
0946: /**
0947: * Toggles the line separator between the three available settings.
0948: * This is used by the status bar.
0949: * @param view We show a message in the view's status bar
0950: * @since jEdit 4.1pre3
0951: */
0952: public void toggleLineSeparator(View view) {
0953: String status = null;
0954: String lineSep = getStringProperty("lineSeparator");
0955: if ("\n".equals(lineSep)) {
0956: status = "windows";
0957: lineSep = "\r\n";
0958: } else if ("\r\n".equals(lineSep)) {
0959: status = "mac";
0960: lineSep = "\r";
0961: } else if ("\r".equals(lineSep)) {
0962: status = "unix";
0963: lineSep = "\n";
0964: }
0965: view.getStatus().setMessageAndClear(
0966: jEdit.getProperty("view.status.linesep-changed",
0967: new String[] { jEdit.getProperty("lineSep."
0968: + status) }));
0969: setProperty("lineSeparator", lineSep);
0970: setDirty(true);
0971: propertiesChanged();
0972: } //}}}
0973:
0974: //{{{ getContextSensitiveProperty() method
0975: /**
0976: * Some settings, like comment start and end strings, can
0977: * vary between different parts of a buffer (HTML text and inline
0978: * JavaScript, for example).
0979: * @param offset The offset
0980: * @param name The property name
0981: * @since jEdit 4.0pre3
0982: */
0983: public String getContextSensitiveProperty(int offset, String name) {
0984: Object value = super .getContextSensitiveProperty(offset, name);
0985:
0986: if (value == null) {
0987: ParserRuleSet rules = getRuleSetAtOffset(offset);
0988:
0989: value = jEdit.getMode(rules.getModeName())
0990: .getProperty(name);
0991:
0992: if (value == null)
0993: value = mode.getProperty(name);
0994: }
0995:
0996: if (value == null)
0997: return null;
0998: else
0999: return String.valueOf(value);
1000: } //}}}
1001:
1002: //}}}
1003:
1004: //{{{ Edit modes, syntax highlighting
1005:
1006: //{{{ setMode() method
1007: /**
1008: * Sets this buffer's edit mode by calling the accept() method
1009: * of each registered edit mode.
1010: */
1011: public void setMode() {
1012: String userMode = getStringProperty("mode");
1013: if (userMode != null) {
1014: unsetProperty("mode");
1015: Mode m = ModeProvider.instance.getMode(userMode);
1016: if (m != null) {
1017: setMode(m);
1018: return;
1019: }
1020: }
1021:
1022: String firstLine = getLineText(0);
1023:
1024: Mode mode = ModeProvider.instance.getModeForFile(name,
1025: firstLine);
1026: if (mode != null) {
1027: setMode(mode);
1028: return;
1029: }
1030:
1031: Mode defaultMode = jEdit.getMode(jEdit
1032: .getProperty("buffer.defaultMode"));
1033: if (defaultMode == null)
1034: defaultMode = jEdit.getMode("text");
1035: setMode(defaultMode);
1036: } //}}}
1037:
1038: //}}}
1039:
1040: //{{{ Deprecated methods
1041:
1042: //{{{ putProperty() method
1043: /**
1044: * @deprecated Call <code>setProperty()</code> instead.
1045: */
1046: public void putProperty(Object name, Object value) {
1047: // for backwards compatibility
1048: if (!(name instanceof String))
1049: return;
1050:
1051: setProperty((String) name, value);
1052: } //}}}
1053:
1054: //{{{ putBooleanProperty() method
1055: /**
1056: * @deprecated Call <code>setBooleanProperty()</code> instead
1057: */
1058: public void putBooleanProperty(String name, boolean value) {
1059: setBooleanProperty(name, value);
1060: } //}}}
1061:
1062: //{{{ markTokens() method
1063: /**
1064: * @deprecated Use org.gjt.sp.jedit.syntax.DefaultTokenHandler instead
1065: */
1066: public static class TokenList extends DefaultTokenHandler {
1067: public Token getFirstToken() {
1068: return getTokens();
1069: }
1070: }
1071:
1072: /**
1073: * @deprecated Use the other form of <code>markTokens()</code> instead
1074: */
1075: public TokenList markTokens(int lineIndex) {
1076: TokenList list = new TokenList();
1077: markTokens(lineIndex, list);
1078: return list;
1079: } //}}}
1080:
1081: //{{{ insertString() method
1082: /**
1083: * @deprecated Call <code>insert()</code> instead.
1084: */
1085: public void insertString(int offset, String str, AttributeSet attr) {
1086: insert(offset, str);
1087: } //}}}
1088:
1089: //{{{ getFile() method
1090: /**
1091: * @deprecated Do not call this method, use {@link #getPath()}
1092: * instead.
1093: */
1094: public File getFile() {
1095: return file;
1096: } //}}}
1097:
1098: //}}}
1099:
1100: //{{{ Marker methods
1101:
1102: //{{{ getMarkers() method
1103: /**
1104: * Returns a vector of markers.
1105: * @since jEdit 3.2pre1
1106: */
1107: public Vector<Marker> getMarkers() {
1108: return markers;
1109: } //}}}
1110:
1111: //{{{ getMarkerStatusPrompt() method
1112: /**
1113: * Returns the status prompt for the given marker action. Only
1114: * intended to be called from <code>actions.xml</code>.
1115: * @since jEdit 4.2pre2
1116: */
1117: public String getMarkerStatusPrompt(String action) {
1118: return jEdit.getProperty("view.status." + action,
1119: new String[] { getMarkerNameString() });
1120: } //}}}
1121:
1122: //{{{ getMarkerNameString() method
1123: /**
1124: * Returns a string of all set markers, used by the status bar
1125: * (eg, "a b $ % ^").
1126: * @since jEdit 4.2pre2
1127: */
1128: public String getMarkerNameString() {
1129: StringBuilder buf = new StringBuilder();
1130: for (int i = 0; i < markers.size(); i++) {
1131: Marker marker = markers.get(i);
1132: if (marker.getShortcut() != '\0') {
1133: if (buf.length() != 0)
1134: buf.append(' ');
1135: buf.append(marker.getShortcut());
1136: }
1137: }
1138:
1139: if (buf.length() == 0)
1140: return jEdit.getProperty("view.status.no-markers");
1141: else
1142: return buf.toString();
1143: } //}}}
1144:
1145: //{{{ addOrRemoveMarker() method
1146: /**
1147: * If a marker is set on the line of the position, it is removed. Otherwise
1148: * a new marker with the specified shortcut is added.
1149: * @param pos The position of the marker
1150: * @param shortcut The shortcut ('\0' if none)
1151: * @since jEdit 3.2pre5
1152: */
1153: public void addOrRemoveMarker(char shortcut, int pos) {
1154: int line = getLineOfOffset(pos);
1155: if (getMarkerAtLine(line) != null)
1156: removeMarker(line);
1157: else
1158: addMarker(shortcut, pos);
1159: } //}}}
1160:
1161: //{{{ addMarker() method
1162: /**
1163: * Adds a marker to this buffer.
1164: * @param pos The position of the marker
1165: * @param shortcut The shortcut ('\0' if none)
1166: * @since jEdit 3.2pre1
1167: */
1168: public void addMarker(char shortcut, int pos) {
1169: Marker markerN = new Marker(this , shortcut, pos);
1170: boolean added = false;
1171:
1172: // don't sort markers while buffer is being loaded
1173: if (isLoaded()) {
1174: setFlag(MARKERS_CHANGED, true);
1175:
1176: markerN.createPosition();
1177:
1178: for (int i = 0; i < markers.size(); i++) {
1179: Marker marker = markers.get(i);
1180: if (shortcut != '\0'
1181: && marker.getShortcut() == shortcut)
1182: marker.setShortcut('\0');
1183:
1184: if (marker.getPosition() == pos) {
1185: markers.removeElementAt(i);
1186: i--;
1187: }
1188: }
1189:
1190: for (int i = 0; i < markers.size(); i++) {
1191: Marker marker = markers.get(i);
1192: if (marker.getPosition() > pos) {
1193: markers.insertElementAt(markerN, i);
1194: added = true;
1195: break;
1196: }
1197: }
1198: }
1199:
1200: if (!added)
1201: markers.addElement(markerN);
1202:
1203: if (isLoaded() && !getFlag(TEMPORARY)) {
1204: EditBus.send(new BufferUpdate(this , null,
1205: BufferUpdate.MARKERS_CHANGED));
1206: }
1207: } //}}}
1208:
1209: //{{{ getMarkerInRange() method
1210: /**
1211: * Returns the first marker within the specified range.
1212: * @param start The start offset
1213: * @param end The end offset
1214: * @since jEdit 4.0pre4
1215: */
1216: public Marker getMarkerInRange(int start, int end) {
1217: for (int i = 0; i < markers.size(); i++) {
1218: Marker marker = markers.get(i);
1219: int pos = marker.getPosition();
1220: if (pos >= start && pos < end)
1221: return marker;
1222: }
1223:
1224: return null;
1225: } //}}}
1226:
1227: //{{{ getMarkerAtLine() method
1228: /**
1229: * Returns the first marker at the specified line, or <code>null</code>
1230: * if there is none.
1231: * @param line The line number
1232: * @since jEdit 3.2pre2
1233: */
1234: public Marker getMarkerAtLine(int line) {
1235: for (int i = 0; i < markers.size(); i++) {
1236: Marker marker = markers.get(i);
1237: if (getLineOfOffset(marker.getPosition()) == line)
1238: return marker;
1239: }
1240:
1241: return null;
1242: } //}}}
1243:
1244: //{{{ removeMarker() method
1245: /**
1246: * Removes all markers at the specified line.
1247: * @param line The line number
1248: * @since jEdit 3.2pre2
1249: */
1250: public void removeMarker(int line) {
1251: for (int i = 0; i < markers.size(); i++) {
1252: Marker marker = markers.get(i);
1253: if (getLineOfOffset(marker.getPosition()) == line) {
1254: setFlag(MARKERS_CHANGED, true);
1255: marker.removePosition();
1256: markers.removeElementAt(i);
1257: i--;
1258: }
1259: }
1260:
1261: EditBus.send(new BufferUpdate(this , null,
1262: BufferUpdate.MARKERS_CHANGED));
1263: } //}}}
1264:
1265: //{{{ removeAllMarkers() method
1266: /**
1267: * Removes all defined markers.
1268: * @since jEdit 2.6pre1
1269: */
1270: public void removeAllMarkers() {
1271: setFlag(MARKERS_CHANGED, true);
1272:
1273: for (int i = 0; i < markers.size(); i++)
1274: markers.get(i).removePosition();
1275:
1276: markers.removeAllElements();
1277:
1278: if (isLoaded()) {
1279: EditBus.send(new BufferUpdate(this , null,
1280: BufferUpdate.MARKERS_CHANGED));
1281: }
1282: } //}}}
1283:
1284: //{{{ getMarker() method
1285: /**
1286: * Returns the marker with the specified shortcut.
1287: * @param shortcut The shortcut
1288: * @since jEdit 3.2pre2
1289: */
1290: public Marker getMarker(char shortcut) {
1291: Enumeration<Marker> e = markers.elements();
1292: while (e.hasMoreElements()) {
1293: Marker marker = e.nextElement();
1294: if (marker.getShortcut() == shortcut)
1295: return marker;
1296: }
1297: return null;
1298: } //}}}
1299:
1300: //{{{ getMarkersPath() method
1301: /**
1302: * Returns the path for this buffer's markers file
1303: * @param vfs The appropriate VFS
1304: * @since jEdit 4.3pre7
1305: * @deprecated it will fail if you save to another VFS. use {@link #getMarkersPath(VFS, String)}
1306: */
1307: @Deprecated
1308: public String getMarkersPath(VFS vfs) {
1309: return getMarkersPath(vfs, path);
1310: } //}}}
1311:
1312: //{{{ getMarkersPath() method
1313: /**
1314: * Returns the path for this buffer's markers file
1315: * @param vfs The appropriate VFS
1316: * @param path the path of the buffer, it can be different from the field
1317: * when using save-as
1318: * @since jEdit 4.3pre10
1319: */
1320: public static String getMarkersPath(VFS vfs, String path) {
1321: return vfs.getParentOfPath(path) + '.' + vfs.getFileName(path)
1322: + ".marks";
1323: } //}}}
1324:
1325: //{{{ updateMarkersFile() method
1326: /**
1327: * Save the markers file, or delete it when there are mo markers left
1328: * Handling markers is now independent from saving the buffer.
1329: * Changing markers will not set the buffer dirty any longer.
1330: * @param view The current view
1331: * @since jEdit 4.3pre7
1332: */
1333: public boolean updateMarkersFile(View view) {
1334: if (!markersChanged())
1335: return true;
1336: // adapted from VFS.save
1337: VFS vfs = VFSManager.getVFSForPath(getPath());
1338: if (((vfs.getCapabilities() & VFS.WRITE_CAP) == 0)
1339: || (!vfs.isMarkersFileSupported())) {
1340: VFSManager.error(view, path, "vfs.not-supported.save",
1341: new String[] { "markers file" });
1342: return false;
1343: }
1344: Object session = vfs.createVFSSession(path, view);
1345: if (session == null)
1346: return false;
1347: VFSManager.runInWorkThread(new MarkersSaveRequest(view, this ,
1348: session, vfs, path));
1349: return true;
1350: } //}}}
1351:
1352: //{{{ markersChanged() method
1353: /**
1354: * Return true when markers have changed and the markers file needs
1355: * to be updated
1356: * @since jEdit 4.3pre7
1357: */
1358: public boolean markersChanged() {
1359: return getFlag(MARKERS_CHANGED);
1360: } //}}}
1361:
1362: //{{{ setMarkersChanged() method
1363: /**
1364: * Sets/unsets the MARKERS_CHANGED flag
1365: * @since jEdit 4.3pre7
1366: */
1367: public void setMarkersChanged(boolean changed) {
1368: setFlag(MARKERS_CHANGED, changed);
1369: } //}}}
1370:
1371: //}}}
1372:
1373: //{{{ Miscellaneous methods
1374:
1375: //{{{ setWaitSocket() method
1376: /**
1377: * This socket is closed when the buffer is closed.
1378: */
1379: public void setWaitSocket(Socket waitSocket) {
1380: this .waitSocket = waitSocket;
1381: } //}}}
1382:
1383: //{{{ getNext() method
1384: /**
1385: * Returns the next buffer in the list.
1386: */
1387: public Buffer getNext() {
1388: return next;
1389: } //}}}
1390:
1391: //{{{ getPrev() method
1392: /**
1393: * Returns the previous buffer in the list.
1394: */
1395: public Buffer getPrev() {
1396: return prev;
1397: } //}}}
1398:
1399: //{{{ getIndex() method
1400: /**
1401: * Returns the position of this buffer in the buffer list.
1402: */
1403: public int getIndex() {
1404: int count = 0;
1405: Buffer buffer = prev;
1406: while (true) {
1407: if (buffer == null)
1408: break;
1409: count++;
1410: buffer = buffer.prev;
1411: }
1412: return count;
1413: } //}}}
1414:
1415: //{{{ toString() method
1416: /**
1417: * Returns a string representation of this buffer.
1418: * This simply returns the path name.
1419: */
1420: public String toString() {
1421: return name + " (" + directory + ')';
1422: } //}}}
1423:
1424: //}}}
1425:
1426: //{{{ Package-private members
1427: /** The previous buffer in the list. */
1428: Buffer prev;
1429: /** The next buffer in the list. */
1430: Buffer next;
1431:
1432: //{{{ Buffer constructor
1433: Buffer(String path, boolean newFile, boolean temp, Hashtable props) {
1434: super (props);
1435:
1436: markers = new Vector<Marker>();
1437:
1438: setFlag(TEMPORARY, temp);
1439:
1440: // this must be called before any EditBus messages are sent
1441: setPath(path);
1442:
1443: /* Magic: UNTITLED is only set if newFile param to
1444: * constructor is set, NEW_FILE is also set if file
1445: * doesn't exist on disk.
1446: *
1447: * This is so that we can tell apart files created
1448: * with jEdit.newFile(), and those that just don't
1449: * exist on disk.
1450: *
1451: * Why do we need to tell the difference between the
1452: * two? jEdit.addBufferToList() checks if the only
1453: * opened buffer is an untitled buffer, and if so,
1454: * replaces it with the buffer to add. We don't want
1455: * this behavior to occur with files that don't
1456: * exist on disk; only untitled ones.
1457: */
1458: setFlag(UNTITLED, newFile);
1459: setFlag(NEW_FILE, newFile);
1460: setFlag(AUTORELOAD, jEdit.getBooleanProperty("autoReload"));
1461: setFlag(AUTORELOAD_DIALOG, jEdit
1462: .getBooleanProperty("autoReloadDialog"));
1463: } //}}}
1464:
1465: //{{{ commitTemporary() method
1466: void commitTemporary() {
1467: setFlag(TEMPORARY, false);
1468:
1469: finishLoading();
1470: } //}}}
1471:
1472: //{{{ close() method
1473: void close() {
1474: setFlag(CLOSED, true);
1475:
1476: if (autosaveFile != null)
1477: autosaveFile.delete();
1478:
1479: // notify clients with -wait
1480: if (waitSocket != null) {
1481: try {
1482: waitSocket.getOutputStream().write('\0');
1483: waitSocket.getOutputStream().flush();
1484: waitSocket.getInputStream().close();
1485: waitSocket.getOutputStream().close();
1486: waitSocket.close();
1487: } catch (IOException io) {
1488: //Log.log(Log.ERROR,this,io);
1489: }
1490: }
1491: } //}}}
1492:
1493: //}}}
1494:
1495: //{{{ Private members
1496:
1497: //{{{ Flags
1498:
1499: //{{{ setFlag() method
1500: private void setFlag(int flag, boolean value) {
1501: if (value)
1502: flags |= (1 << flag);
1503: else
1504: flags &= ~(1 << flag);
1505: } //}}}
1506:
1507: //{{{ getFlag() method
1508: private boolean getFlag(int flag) {
1509: int mask = (1 << flag);
1510: return (flags & mask) == mask;
1511: } //}}}
1512:
1513: //{{{ Flag values
1514: private static final int CLOSED = 0;
1515: private static final int NEW_FILE = 3;
1516: private static final int UNTITLED = 4;
1517: private static final int AUTOSAVE_DIRTY = 5;
1518: private static final int AUTORELOAD = 6;
1519: private static final int AUTORELOAD_DIALOG = 7;
1520: private static final int TEMPORARY = 10;
1521: private static final int MARKERS_CHANGED = 12;
1522: //}}}
1523:
1524: private int flags;
1525:
1526: //}}}
1527:
1528: //{{{ Instance variables
1529: private String path;
1530: private String symlinkPath;
1531: private String name;
1532: private String directory;
1533: private File file;
1534: private File autosaveFile;
1535: private long modTime;
1536:
1537: private final Vector<Marker> markers;
1538:
1539: private Socket waitSocket;
1540:
1541: //}}}
1542:
1543: //{{{ setPath() method
1544: private void setPath(String path) {
1545: View[] views = jEdit.getViews();
1546: for (int i = 0; i < views.length; i++) {
1547: View view = views[i];
1548: EditPane[] editPanes = view.getEditPanes();
1549: for (int j = 0; j < editPanes.length; j++)
1550: editPanes[j].bufferRenamed(this .path, path);
1551: }
1552:
1553: this .path = path;
1554: VFS vfs = VFSManager.getVFSForPath(path);
1555: if ((vfs.getCapabilities() & VFS.WRITE_CAP) == 0)
1556: setFileReadOnly(true);
1557: name = vfs.getFileName(path);
1558: directory = vfs.getParentOfPath(path);
1559:
1560: if (vfs instanceof FileVFS) {
1561: file = new File(path);
1562: symlinkPath = MiscUtilities.resolveSymlinks(path);
1563:
1564: // if we don't do this, the autosave file won't be
1565: // deleted after a save as
1566: if (autosaveFile != null)
1567: autosaveFile.delete();
1568: autosaveFile = new File(file.getParent(), '#' + name + '#');
1569: } else {
1570: // I wonder if the lack of this broke anything in the
1571: // past?
1572: file = null;
1573: autosaveFile = null;
1574: symlinkPath = path;
1575: }
1576: } //}}}
1577:
1578: //{{{ recoverAutosave() method
1579: private boolean recoverAutosave(final View view) {
1580: if (!autosaveFile.canRead())
1581: return false;
1582:
1583: // this method might get called at startup
1584: GUIUtilities.hideSplashScreen();
1585:
1586: final Object[] args = { autosaveFile.getPath() };
1587: int result = GUIUtilities.confirm(view, "autosave-found", args,
1588: JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
1589:
1590: if (result == JOptionPane.YES_OPTION) {
1591: VFSManager.getFileVFS().load(view, this ,
1592: autosaveFile.getPath());
1593:
1594: // show this message when all I/O requests are
1595: // complete
1596: VFSManager.runInAWTThread(new Runnable() {
1597: public void run() {
1598: GUIUtilities.message(view, "autosave-loaded", args);
1599: }
1600: });
1601:
1602: return true;
1603: } else
1604: return false;
1605: } //}}}
1606:
1607: //{{{ checkFileForLoad() method
1608: private boolean checkFileForLoad(View view, VFS vfs, String path) {
1609: if ((vfs.getCapabilities() & VFS.LOW_LATENCY_CAP) != 0) {
1610: Object session = vfs.createVFSSession(path, view);
1611: if (session == null)
1612: return false;
1613:
1614: try {
1615: VFSFile file = vfs._getFile(session, path, view);
1616: if (file == null) {
1617: setNewFile(true);
1618: return true;
1619: }
1620:
1621: if (!file.isReadable()) {
1622: VFSManager.error(view, path, "ioerror.no-read",
1623: null);
1624: setNewFile(false);
1625: return false;
1626: }
1627:
1628: setFileReadOnly(!file.isWriteable());
1629:
1630: if (file.getType() != VFSFile.FILE) {
1631: VFSManager.error(view, path,
1632: "ioerror.open-directory", null);
1633: setNewFile(false);
1634: return false;
1635: }
1636: } catch (IOException io) {
1637: VFSManager.error(view, path, "ioerror",
1638: new String[] { io.toString() });
1639: return false;
1640: } finally {
1641: try {
1642: vfs._endVFSSession(session, view);
1643: } catch (IOException io) {
1644: VFSManager.error(view, path, "ioerror",
1645: new String[] { io.toString() });
1646: return false;
1647: }
1648: }
1649: }
1650:
1651: return true;
1652: } //}}}
1653:
1654: //{{{ checkFileForSave() method
1655: private static boolean checkFileForSave(View view, VFS vfs,
1656: String path) {
1657: if ((vfs.getCapabilities() & VFS.LOW_LATENCY_CAP) != 0) {
1658: Object session = vfs.createVFSSession(path, view);
1659: if (session == null)
1660: return false;
1661:
1662: try {
1663: VFSFile file = vfs._getFile(session, path, view);
1664: if (file == null)
1665: return true;
1666:
1667: if (file.getType() != VFSFile.FILE) {
1668: VFSManager.error(view, path,
1669: "ioerror.save-directory", null);
1670: return false;
1671: }
1672: } catch (IOException io) {
1673: VFSManager.error(view, path, "ioerror",
1674: new String[] { io.toString() });
1675: return false;
1676: } finally {
1677: try {
1678: vfs._endVFSSession(session, view);
1679: } catch (IOException io) {
1680: VFSManager.error(view, path, "ioerror",
1681: new String[] { io.toString() });
1682: return false;
1683: }
1684: }
1685: }
1686:
1687: return true;
1688: } //}}}
1689:
1690: //{{{ finishLoading() method
1691: private void finishLoading() {
1692: parseBufferLocalProperties();
1693: // AHA!
1694: // this is probably the only way to fix this
1695: FoldHandler oldFoldHandler = getFoldHandler();
1696: setMode();
1697:
1698: if (getFoldHandler() == oldFoldHandler) {
1699: // on a reload, the fold handler doesn't change, but
1700: // we still need to re-collapse folds.
1701: // don't do this on initial fold handler creation
1702: invalidateFoldLevels();
1703:
1704: fireFoldHandlerChanged();
1705: }
1706:
1707: // Create marker positions
1708: for (int i = 0; i < markers.size(); i++) {
1709: Marker marker = markers.get(i);
1710: marker.removePosition();
1711: int pos = marker.getPosition();
1712: if (pos > getLength())
1713: marker.setPosition(getLength());
1714: else if (pos < 0)
1715: marker.setPosition(0);
1716: marker.createPosition();
1717: }
1718: } //}}}
1719:
1720: //{{{ finishSaving() method
1721: private void finishSaving(View view, String oldPath,
1722: String oldSymlinkPath, String path, boolean rename,
1723: boolean error) {
1724: //{{{ Set the buffer's path
1725: // Caveat: won't work if save() called with a relative path.
1726: // But I don't think anyone calls it like that anyway.
1727: if (!error && !path.equals(oldPath)) {
1728: Buffer buffer = jEdit.getBuffer(path);
1729:
1730: if (rename) {
1731: /* if we save a file with the same name as one
1732: * that's already open, we presume that we can
1733: * close the existing file, since the user
1734: * would have confirmed the overwrite in the
1735: * 'save as' dialog box anyway */
1736: if (buffer != null && /* can't happen? */
1737: !buffer.getPath().equals(oldPath)) {
1738: buffer.setDirty(false);
1739: jEdit.closeBuffer(view, buffer);
1740: }
1741:
1742: setPath(path);
1743: } else {
1744: /* if we saved over an already open file using
1745: * 'save a copy as', then reload the existing
1746: * buffer */
1747: if (buffer != null && /* can't happen? */
1748: !buffer.getPath().equals(oldPath)) {
1749: buffer.load(view, true);
1750: }
1751: }
1752: } //}}}
1753:
1754: //{{{ Update this buffer for the new path
1755: if (rename) {
1756: if (file != null)
1757: modTime = file.lastModified();
1758:
1759: if (!error) {
1760: // we do a write lock so that the
1761: // autosave, which grabs a read lock,
1762: // is not executed between the
1763: // deletion of the autosave file
1764: // and clearing of the dirty flag
1765: try {
1766: writeLock();
1767:
1768: if (autosaveFile != null)
1769: autosaveFile.delete();
1770:
1771: setFlag(AUTOSAVE_DIRTY, false);
1772: setFileReadOnly(false);
1773: setFlag(NEW_FILE, false);
1774: setFlag(UNTITLED, false);
1775: super .setDirty(false);
1776:
1777: // this ensures that undo can clear
1778: // the dirty flag properly when all
1779: // edits up to a save are undone
1780: undoMgr.bufferSaved();
1781: } finally {
1782: writeUnlock();
1783: }
1784:
1785: parseBufferLocalProperties();
1786:
1787: if (!getPath().equals(oldPath)) {
1788: if (!isTemporary())
1789: jEdit.updatePosition(oldSymlinkPath, this );
1790: setMode();
1791: } else {
1792: // if user adds mode buffer-local property
1793: String newMode = getStringProperty("mode");
1794: if (newMode != null
1795: && !newMode.equals(getMode().getName()))
1796: setMode();
1797: else
1798: propertiesChanged();
1799: }
1800:
1801: if (!isTemporary()) {
1802: EditBus.send(new BufferUpdate(this , view,
1803: BufferUpdate.DIRTY_CHANGED));
1804:
1805: // new message type introduced in 4.0pre4
1806: EditBus.send(new BufferUpdate(this , view,
1807: BufferUpdate.SAVED));
1808: }
1809: }
1810: } //}}}
1811: } //}}}
1812:
1813: //{{{ editSyntaxStyle() method
1814: /**
1815: * Edit the syntax style of the token under the caret.
1816: *
1817: * @param textArea the textarea where your caret is
1818: * @since jEdit 4.3pre11
1819: */
1820: void editSyntaxStyle(JEditTextArea textArea) {
1821: int lineNum = textArea.getCaretLine();
1822: int start = getLineStartOffset(lineNum);
1823: int position = textArea.getCaretPosition();
1824:
1825: DefaultTokenHandler tokenHandler = new DefaultTokenHandler();
1826: markTokens(lineNum, tokenHandler);
1827: Token token = tokenHandler.getTokens();
1828:
1829: while (token.id != Token.END) {
1830: int next = start + token.length;
1831: if (start <= position && next > position)
1832: break;
1833: start = next;
1834: token = token.next;
1835: }
1836: if (token.id == Token.END || token.id == Token.NULL) {
1837: JOptionPane.showMessageDialog(jEdit.getActiveView(), jEdit
1838: .getProperty("syntax-style-no-token.message"),
1839: jEdit.getProperty("syntax-style-no-token.title"),
1840: JOptionPane.PLAIN_MESSAGE);
1841: return;
1842: }
1843: String typeName = Token.tokenToString(token.id);
1844: String property = "view.style." + typeName.toLowerCase();
1845: SyntaxStyle currentStyle = GUIUtilities.parseStyle(jEdit
1846: .getProperty(property), "Dialog", 12);
1847: SyntaxStyle style = new StyleEditor(jEdit.getActiveView(),
1848: currentStyle, typeName).getStyle();
1849: if (style != null)
1850: jEdit.setProperty(property, GUIUtilities
1851: .getStyleString(style));
1852: jEdit.propertiesChanged();
1853: } //}}}
1854: //}}}
1855: }
|