From: <ez...@us...> - 2017-05-13 21:38:28
|
Revision: 24675 http://sourceforge.net/p/jedit/svn/24675 Author: ezust Date: 2017-05-13 21:38:25 +0000 (Sat, 13 May 2017) Log Message: ----------- Autosave Untitled Buffers fixed (Patch #596 - Hrotko Gabor) Modified Paths: -------------- jEdit/trunk/doc/CHANGES.txt jEdit/trunk/org/gjt/sp/jedit/Buffer.java jEdit/trunk/org/gjt/sp/jedit/MiscUtilities.java jEdit/trunk/org/gjt/sp/jedit/PerspectiveManager.java jEdit/trunk/org/gjt/sp/jedit/View.java jEdit/trunk/org/gjt/sp/jedit/bufferset/BufferSet.java jEdit/trunk/org/gjt/sp/jedit/bufferset/BufferSetManager.java jEdit/trunk/org/gjt/sp/jedit/jEdit.java jEdit/trunk/org/gjt/sp/jedit/perspective.dtd Modified: jEdit/trunk/doc/CHANGES.txt =================================================================== --- jEdit/trunk/doc/CHANGES.txt 2017-05-04 19:43:49 UTC (rev 24674) +++ jEdit/trunk/doc/CHANGES.txt 2017-05-13 21:38:25 UTC (rev 24675) @@ -4,13 +4,15 @@ {{{ Version 5.5pre1 -Thanks to AdamS, Wim West, M. Cesar R. Lacruz, Egor Abramovich and Alan Ezust -for contributing to this release. +Thanks to AdamS, Wim West, M. Cesar R. Lacruz, Egor Abramovich, +Hrotkó Gábor, and Alan Ezust for contributing to this release. {{{ Bug Fixes - command line behavior for +line fixed (Patch #590 - Wim West) +- Autosave Untitled Buffers fixed (Patch #596 - Hrotkó Gábor) + }}} {{{ UI Improvements Modified: jEdit/trunk/org/gjt/sp/jedit/Buffer.java =================================================================== --- jEdit/trunk/org/gjt/sp/jedit/Buffer.java 2017-05-04 19:43:49 UTC (rev 24674) +++ jEdit/trunk/org/gjt/sp/jedit/Buffer.java 2017-05-13 21:38:25 UTC (rev 24675) @@ -34,7 +34,6 @@ import java.util.Vector; import javax.swing.*; -import javax.swing.SwingWorker.StateValue; import javax.swing.text.Segment; import org.gjt.sp.jedit.browser.VFSBrowser; @@ -43,7 +42,6 @@ import org.gjt.sp.jedit.buffer.JEditBuffer; import org.gjt.sp.jedit.bufferio.BufferAutosaveRequest; import org.gjt.sp.jedit.bufferio.BufferIORequest; -import org.gjt.sp.jedit.bufferio.IoTask; import org.gjt.sp.jedit.bufferio.MarkersSaveRequest; import org.gjt.sp.jedit.bufferset.BufferSet; import org.gjt.sp.jedit.gui.DockableWindowManager; @@ -197,7 +195,10 @@ final boolean loadAutosave; - if(reload || !getFlag(NEW_FILE)) + boolean autosaveUntitled = jEdit.getBooleanProperty("autosaveUntitled"); + + // for untitled: re-read autosave file if enabled + if(reload || !getFlag(NEW_FILE) || (isUntitled() && autosaveUntitled)) { if(file != null) modTime = file.lastModified(); @@ -643,7 +644,8 @@ // because for a moment newModTime will be greater than // oldModTime, due to the multithreading // - only supported on local file system - if(!isPerformingIO() && file != null && !getFlag(NEW_FILE)) + // - for untitled, do not check + if(!isPerformingIO() && file != null && !getFlag(NEW_FILE) && !isUntitled()) { boolean newReadOnly = file.exists() && !file.canWrite(); if(newReadOnly != isFileReadOnly()) @@ -894,6 +896,16 @@ return getFlag(UNTITLED); } //}}} + //{{{ setUntitled() method + /** + * + * @param untitled untitled value to set + */ + protected void setUntitled(boolean untitled) + { + setFlag(UNTITLED, untitled); + } //}}} + //{{{ setDirty() method /** * Sets the 'dirty' (changed since last save) flag of this buffer. @@ -906,7 +918,8 @@ d = false; if (d && getLength() == initialLength) { - if (jEdit.getBooleanProperty("useMD5forDirtyCalculation")) + // for untitled, do not check if the content existed before + if (jEdit.getBooleanProperty("useMD5forDirtyCalculation") && !isUntitled()) d = !Arrays.equals(calculateHash(), md5hash); } super.setDirty(d); @@ -1643,6 +1656,12 @@ //{{{ Buffer constructor Buffer(String path, boolean newFile, boolean temp, Map props) { + this(path, newFile, temp, props, false); + } + + //{{{ Buffer constructor + Buffer(String path, boolean newFile, boolean temp, Map props, boolean untitled) + { super(props); textTokenMarker = jEdit.getMode("text").getTokenMarker(); markers = new Vector<Marker>(); @@ -1652,22 +1671,7 @@ // this must be called before any EditBus messages are sent setPath(path); - /* Magic: UNTITLED is only set if newFile param to - * constructor is set, NEW_FILE is also set if file - * doesn't exist on disk. - * - * This is so that we can tell apart files created - * with jEdit.newFile(), and those that just don't - * exist on disk. - * - * Why do we need to tell the difference between the - * two? jEdit.addBufferToList() checks if the only - * opened buffer is an untitled buffer, and if so, - * replaces it with the buffer to add. We don't want - * this behavior to occur with files that don't - * exist on disk; only untitled ones. - */ - setFlag(UNTITLED,newFile); + setFlag(UNTITLED,untitled); setFlag(NEW_FILE,newFile); setFlag(AUTORELOAD,jEdit.getBooleanProperty("autoReload")); setFlag(AUTORELOAD_DIALOG,jEdit.getBooleanProperty("autoReloadDialog")); @@ -1686,11 +1690,29 @@ //{{{ close() method void close() { + close(false); + } + + //{{{ close() method + /** + * close the buffer + * @param doNotSave when true, we do not want to keep the autosave even for untitled + * e.g.: we closed the buffer by hand + */ + void close(boolean doNotSave) + { setFlag(CLOSED,true); + boolean autosaveUntitled = jEdit.getBooleanProperty("autosaveUntitled"); - if(autosaveFile != null) + if(autosaveFile != null && (doNotSave || !(isUntitled() && autosaveUntitled))) autosaveFile.delete(); + // close az untitled buffer, but need to autosavesave + // except we close it manually and do not want to save + if ( !doNotSave && isUntitled() && autosaveUntitled ) { + autosave(); + } + // notify clients with -wait if(waitSocket != null) { @@ -1882,6 +1904,13 @@ if((vfs.getCapabilities() & VFS.WRITE_CAP) == 0) setFileReadOnly(true); name = vfs.getFileName(path); + // clean up buffer name + // # is for autosave, e.g. reloading autosaved untitled, remove it from the buffer's name + if ( name.startsWith("#") ) + name = name.substring(1, name.length()); + if ( name.endsWith("#") ) + name = name.substring(0, name.length()-1); + directory = vfs.getParentOfPath(path); if(vfs instanceof FileVFS) @@ -1915,9 +1944,22 @@ // this method might get called at startup GUIUtilities.hideSplashScreen(); + boolean autosaveUntitled = jEdit.getBooleanProperty("autosaveUntitled"); + final Object[] args = { autosaveFile.getPath() }; - int result = GUIUtilities.confirm(view,"autosave-found",args, + + int result; + // if it was an untitled autosave, recover without question + if (isUntitled() && autosaveUntitled){ + boolean untitledOrigi = isUntitled(); + VFSManager.getFileVFS().load(view,this,autosaveFile.getPath()); + // preserve isUntitled (not the best solution) + setUntitled(untitledOrigi); + return true; + } else { + result = GUIUtilities.confirm(view,"autosave-found",args, JOptionPane.YES_NO_OPTION,JOptionPane.WARNING_MESSAGE); + } if(result == JOptionPane.YES_OPTION) { Modified: jEdit/trunk/org/gjt/sp/jedit/MiscUtilities.java =================================================================== --- jEdit/trunk/org/gjt/sp/jedit/MiscUtilities.java 2017-05-04 19:43:49 UTC (rev 24674) +++ jEdit/trunk/org/gjt/sp/jedit/MiscUtilities.java 2017-05-13 21:38:25 UTC (rev 24675) @@ -668,6 +668,28 @@ } }// }}} + //{{{ prepareAutosaveDirectory method + /** + * Prepares the directory to autosave the specified file. + * If the path does not exists, + * then the current directory is used, but only for local files. + * The directory is created if not exists. + * @param path path to the base dir + * @return Backup directory. <code>null</code> is returned for + * non-local files if no backup directory is specified in properties. + * @since 5.0pre1 + */ + public static File prepareAutosaveDirectory(String path) + { + String directory = MiscUtilities.constructPath(path, "autosave"); + File dir = new File(directory); + + if (!dir.exists()) + dir.mkdirs(); + + return dir; + }// }}} + //{{{ prepareBackupDirectory method /** * Prepares the directory to backup the specified file. @@ -675,6 +697,7 @@ * If there is no dedicated backup directory specified by props, * then the current directory is used, but only for local files. * The directory is created if not exists. + * @param path path to the base dir * @return Backup directory. <code>null</code> is returned for * non-local files if no backup directory is specified in properties. * @since 5.0pre1 Modified: jEdit/trunk/org/gjt/sp/jedit/PerspectiveManager.java =================================================================== --- jEdit/trunk/org/gjt/sp/jedit/PerspectiveManager.java 2017-05-04 19:43:49 UTC (rev 24674) +++ jEdit/trunk/org/gjt/sp/jedit/PerspectiveManager.java 2017-05-13 21:38:25 UTC (rev 24675) @@ -23,12 +23,9 @@ package org.gjt.sp.jedit; import java.io.Closeable; -import java.io.StreamTokenizer; -import java.io.StringReader; import java.io.IOException; import java.util.Collection; import java.util.LinkedList; -import java.util.Stack; import org.gjt.sp.util.IOUtilities; import org.gjt.sp.util.Log; @@ -126,11 +123,12 @@ if(jEdit.getBufferCount() == 0) return; + boolean autosaveUntitled = jEdit.getBooleanProperty("autosaveUntitled"); Buffer[] buffers = jEdit.getBuffers(); Collection<Buffer> savedBuffers = new LinkedList<Buffer>(); for (Buffer buffer: buffers) { - if (!buffer.isNewFile()) + if (!buffer.isNewFile() || (buffer.isUntitled() && autosaveUntitled)) { savedBuffers.add(buffer); } @@ -155,12 +153,23 @@ for (Buffer buffer: savedBuffers) { + if ( buffer.isUntitled() && !autosaveUntitled ) { + // skip buffer if untitled, and we do not want to save it + continue; + } out.write("<BUFFER AUTORELOAD=\""); out.write(buffer.getAutoReload() ? "TRUE" : "FALSE"); out.write("\" AUTORELOAD_DIALOG=\""); out.write(buffer.getAutoReloadDialog() ? "TRUE" : "FALSE"); + out.write("\" UNTITLED=\""); + out.write(buffer.isUntitled()? "TRUE" : "FALSE"); out.write("\">"); - out.write(XMLUtilities.charsToEntities(buffer.getPath(), false)); + + // for untitled, we only have the autosave file + out.write(XMLUtilities.charsToEntities( + buffer.isUntitled()?buffer.getAutosaveFile().getPath():buffer.getPath(), + false)); + out.write("</BUFFER>"); out.write(lineSep); } @@ -263,7 +272,7 @@ View.ViewConfig config; boolean restoreFiles; boolean restoreSplits; - String autoReload, autoReloadDialog; + String autoReload, autoReloadDialog, untitled; PerspectiveHandler(boolean restoreFiles) { @@ -311,6 +320,8 @@ autoReload = value; else if(aname.equals("AUTORELOAD_DIALOG")) autoReloadDialog = value; + else if(aname.equals("UNTITLED")) + untitled = value; } /** @@ -335,7 +346,12 @@ { if (restoreFiles && !skipRemote(charData.toString())) { - Buffer restored = jEdit.openTemporary(null,null, charData.toString(), false); + boolean bufferUntitled = false; + if(untitled != null) { + bufferUntitled = "TRUE".equals(untitled); + } + + Buffer restored = jEdit.openTemporary(null,null, charData.toString(), bufferUntitled, null, bufferUntitled); // if the autoReload attributes are not present, don't set anything // it's sufficient to check whether they are present on the first BUFFER element if (restored != null) @@ -344,6 +360,8 @@ restored.setAutoReload("TRUE".equals(autoReload)); if(autoReloadDialog != null) restored.setAutoReloadDialog("TRUE".equals(autoReloadDialog)); + if(untitled != null) + restored.setUntitled(bufferUntitled); jEdit.commitTemporary(restored); } } Modified: jEdit/trunk/org/gjt/sp/jedit/View.java =================================================================== --- jEdit/trunk/org/gjt/sp/jedit/View.java 2017-05-04 19:43:49 UTC (rev 24674) +++ jEdit/trunk/org/gjt/sp/jedit/View.java 2017-05-13 21:38:25 UTC (rev 24675) @@ -1467,6 +1467,7 @@ */ boolean confirmToCloseDirty() { + boolean autosaveUntitled = jEdit.getBooleanProperty("autosaveUntitled"); Set<Buffer> checkingBuffers = getOpenBuffers(); for (View view: jEdit.getViews()) { @@ -1478,7 +1479,7 @@ } for (Buffer buffer: checkingBuffers) { - if (buffer.isDirty()) + if (buffer.isDirty() && !(buffer.isUntitled() && autosaveUntitled)) { return new CloseDialog(this, checkingBuffers).isOK(); } @@ -1675,21 +1676,24 @@ } else { + boolean autosaveUntitled = jEdit.getBooleanProperty("autosaveUntitled"); + // the component is an editPane EditPane editPane = (EditPane) component; splitConfig.append('"'); + Buffer editPaneBuffer = editPane.getBuffer(); splitConfig.append(StandardUtilities.charsToEscapes( - editPane.getBuffer().getPath())); + editPaneBuffer.isUntitled()?editPaneBuffer.getAutosaveFile().getPath():editPaneBuffer.getPath())); splitConfig.append("\" buffer"); BufferSet bufferSet = editPane.getBufferSet(); Buffer[] buffers = bufferSet.getAllBuffers(); for (Buffer buffer : buffers) { - if (!buffer.isNewFile()) + if (!buffer.isNewFile() || (buffer.isUntitled() && autosaveUntitled)) { splitConfig.append(" \""); splitConfig.append(StandardUtilities.charsToEscapes( - buffer.getPath())); + buffer.isUntitled()?buffer.getAutosaveFile().getPath():buffer.getPath() )); splitConfig.append("\" buff"); } } Modified: jEdit/trunk/org/gjt/sp/jedit/bufferset/BufferSet.java =================================================================== --- jEdit/trunk/org/gjt/sp/jedit/bufferset/BufferSet.java 2017-05-04 19:43:49 UTC (rev 24674) +++ jEdit/trunk/org/gjt/sp/jedit/bufferset/BufferSet.java 2017-05-13 21:38:25 UTC (rev 24675) @@ -90,18 +90,8 @@ { Log.log(Log.DEBUG, this, hashCode() + " addBufferAt("+buffer+','+position+')'); - Buffer untitledBuffer = null; synchronized (buffers) { - if (buffers.size() == 1) - { - Buffer buf = buffers.get(0); - if (buf.isUntitled() && !buf.isDirty()) - { - untitledBuffer = buf; - } - } - if (sorter != null) { if (buffers.contains(buffer)) @@ -137,11 +127,6 @@ listener.bufferAdded(buffer, position); } - // I don't like this reverse control - if (untitledBuffer != null) - { - jEdit.getBufferSetManager().removeBuffer(this, untitledBuffer); - } } //}}} //{{{ getBuffer() method Modified: jEdit/trunk/org/gjt/sp/jedit/bufferset/BufferSetManager.java =================================================================== --- jEdit/trunk/org/gjt/sp/jedit/bufferset/BufferSetManager.java 2017-05-04 19:43:49 UTC (rev 24674) +++ jEdit/trunk/org/gjt/sp/jedit/bufferset/BufferSetManager.java 2017-05-13 21:38:25 UTC (rev 24675) @@ -349,7 +349,9 @@ } if (parent == null) { - parent = System.getProperty("user.home"); + // autosave files will be in a separated dir + String settingsDirectory = jEdit.getSettingsDirectory(); + parent = MiscUtilities.prepareAutosaveDirectory(settingsDirectory).getPath(); } VFS vfs = VFSManager.getVFSForPath(parent); if ((vfs.getCapabilities() & VFS.WRITE_CAP) == 0) @@ -358,7 +360,7 @@ parent = System.getProperty("user.home"); } Buffer newEmptyBuffer = jEdit.openTemporary(view, parent, - "Untitled-" + untitledCount,true, null); + "Untitled-" + untitledCount,true, true); jEdit.commitTemporary(newEmptyBuffer); return newEmptyBuffer; } //}}} Modified: jEdit/trunk/org/gjt/sp/jedit/jEdit.java =================================================================== --- jEdit/trunk/org/gjt/sp/jedit/jEdit.java 2017-05-04 19:43:49 UTC (rev 24674) +++ jEdit/trunk/org/gjt/sp/jedit/jEdit.java 2017-05-13 21:38:25 UTC (rev 24675) @@ -1706,7 +1706,8 @@ return buffer; } - newBuffer = new Buffer(path,newFile,false,props); + // if it is new, then it is untitled + newBuffer = new Buffer(path,newFile,false,props,newFile); if(!newBuffer.load(view,false)) return null; @@ -1751,7 +1752,35 @@ { return openTemporary(view, parent, path, newFile, null); } + + //{{{ openTemporary() methods /** + * Opens a temporary buffer. A temporary buffer is like a normal + * buffer, except that an event is not fired and the buffer is + * not added to the buffers list. + * <p>If a buffer for the given <code>path</code> was + * already opened in jEdit, then this instance is returned. + * Otherwise jEdit will not store a reference + * to the returned Buffer object. + * <p>This method is thread-safe. + * + * @param view The view to open the file in + * @param parent The parent directory of the file + * @param path The path name of the file + * @param newFile True if the file should not be loaded from disk + * @param untitled is the buffer untitled + * + * @return the buffer, or null if jEdit was unable to load it + * + * @since jEdit 3.2pre10 + */ + public static Buffer openTemporary(View view, String parent, + String path, boolean newFile, boolean untitled) + { + return openTemporary(view, parent, path, newFile, null, untitled); + } + + /** * Opens a temporary buffer. * Details: {@link #openTemporary(View, String, String, boolean)} * @@ -1768,6 +1797,27 @@ public static Buffer openTemporary(View view, String parent, String path, boolean newFile, Hashtable<String, Object> props) { + return openTemporary(view, parent, path, newFile, null, false); + } + + /** + * Opens a temporary buffer. + * Details: {@link #openTemporary(View, String, String, boolean)} + * + * @param view The view to open the file in + * @param parent The parent directory of the file + * @param path The path name of the file + * @param newFile True if the file should not be loaded from disk + * @param props Buffer-local properties to set in the buffer + * @param untitled is the buffer untitled + * + * @return the buffer, or null if jEdit was unable to load it + * + * @since jEdit 4.3pre10 + */ + public static Buffer openTemporary(View view, String parent, + String path, boolean newFile, Hashtable<String, Object> props, boolean untitled) + { if(view != null && parent == null) parent = view.getBuffer().getDirectory(); @@ -1777,7 +1827,12 @@ path = path.substring(5); } + if ( untitled ) { + path = MiscUtilities.constructPath( + MiscUtilities.prepareAutosaveDirectory(settingsDirectory).getPath(),path); + } else { path = MiscUtilities.constructPath(parent,path); + } if(props == null) props = new Hashtable<String, Object>(); @@ -1789,7 +1844,7 @@ if(buffer != null) return buffer; - buffer = new Buffer(path,newFile,true,props); + buffer = new Buffer(path,newFile,true,props,untitled); buffer.setBooleanProperty(Buffer.ENCODING_AUTODETECT, true); if(!buffer.load(view,false)) return null; @@ -1863,6 +1918,7 @@ /** * Creates a new `untitled' file. + * Default directory is <settings_dir>/autosave * * @param editPane The editPane to create the file in * @@ -1871,19 +1927,13 @@ */ public static Buffer newFile(EditPane editPane) { - String path; - - if(editPane != null && editPane.getBuffer() != null) - { - path = editPane.getBuffer().getDirectory(); + File autosaveDir = MiscUtilities.prepareAutosaveDirectory(settingsDirectory); + String path = autosaveDir.getPath(); VFS vfs = VFSManager.getVFSForPath(path); // don't want 'New File' to create a read only buffer // if current file is on SQL VFS or something if((vfs.getCapabilities() & VFS.WRITE_CAP) == 0) path = System.getProperty("user.home"); - } - else - path = null; return newFile(editPane,path); } @@ -1960,6 +2010,7 @@ return false; } + boolean doNotSave = false; if(buffer.isDirty()) { Object[] args = { buffer.getName() }; @@ -1978,12 +2029,20 @@ return false; } } - else if(result != JOptionPane.NO_OPTION) + else if(result != JOptionPane.NO_OPTION) { + // cancel return false; } + else if(result == JOptionPane.NO_OPTION) { + // when we close an untitled buffer, cos we do not want to save it by answering No, + // mark to delete the autosave file + doNotSave = true; + } - _closeBuffer(view,buffer); + } + _closeBuffer(view,buffer, doNotSave); + return true; } //}}} @@ -2053,6 +2112,22 @@ */ public static void _closeBuffer(View view, Buffer buffer) { + _closeBuffer(view, buffer, true); + } + + //{{{ _closeBuffer() method + /** + * Closes the buffer, even if it has unsaved changes. + * @param view The view, may be null + * @param buffer The buffer + * @param doNotSave we do not want to keep the autosave file + * + * @exception NullPointerException if the buffer is null + * + * @since jEdit 2.2pre1 + */ + public static void _closeBuffer(View view, Buffer buffer, boolean doNotSave) + { if(buffer.isClosed()) { // can happen if the user presses C+w twice real @@ -2095,7 +2170,7 @@ removeBufferFromList(buffer); - buffer.close(); + buffer.close(doNotSave); DisplayManager.bufferClosed(buffer); bufferSetManager.removeBuffer(buffer); EditBus.send(new BufferUpdate(buffer,view,BufferUpdate.CLOSED)); @@ -2131,10 +2206,12 @@ boolean saveRecent = !(isExiting && jEdit.getBooleanProperty("restore")); + boolean autosaveUntitled = jEdit.getBooleanProperty("autosaveUntitled"); + Buffer buffer = buffersFirst; while(buffer != null) { - if(buffer.isDirty()) + if(buffer.isDirty() && !( buffer.isUntitled() && autosaveUntitled ) ) { dirty = true; break; @@ -2166,7 +2243,7 @@ while(buffer != null) { - if(!buffer.isNewFile() && saveRecent) + if((!buffer.isNewFile() || (buffer.isUntitled() && autosaveUntitled)) && saveRecent) { Integer _caret = (Integer)buffer.getProperty(Buffer.CARET); int caret = _caret == null ? 0 : _caret.intValue(); @@ -4631,6 +4708,24 @@ if (!view.confirmToCloseDirty()) return false; + if ( getBufferSetManager().getScope().equals(BufferSet.Scope.editpane) + || getBufferSetManager().getScope().equals(BufferSet.Scope.view) ) { + // move the untitled buffers to the next open view's current editpane bufferset (first or last) + View targetView; + if ( view.equals(viewsFirst) ) { + targetView = viewsLast; + } else { + targetView = viewsFirst; + } + BufferSet bufferSet = targetView.getEditPane().getBufferSet(); + for (Buffer buffer : view.getBuffers()) { + if ( buffer.isUntitled() ) { + bufferSet.addBuffer(buffer); + } + } + + } + view.close(); view.dispose(); removeViewFromList(view); Modified: jEdit/trunk/org/gjt/sp/jedit/perspective.dtd =================================================================== --- jEdit/trunk/org/gjt/sp/jedit/perspective.dtd 2017-05-04 19:43:49 UTC (rev 24674) +++ jEdit/trunk/org/gjt/sp/jedit/perspective.dtd 2017-05-13 21:38:25 UTC (rev 24675) @@ -7,7 +7,8 @@ <!ELEMENT BUFFER ( #PCDATA ) > <!ATTLIST BUFFER AUTORELOAD %att-bool; "TRUE" - AUTORELOAD_DIALOG %att-bool; "TRUE" > + AUTORELOAD_DIALOG %att-bool; "TRUE" + UNTITLED %att-bool; "FALSE" > <!-- VIEW element --> <!ELEMENT VIEW ( PANES, GEOMETRY, DOCKING? ) > This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |