Blob Blame Raw
diff -Naur wxPython-src-2.8.10.1-orig/wxPython/setup.py wxPython-src-2.8.10.1/wxPython/setup.py
--- wxPython-src-2.8.10.1-orig/wxPython/setup.py        2009-06-06 14:43:00.000000000 -0400
+++ wxPython-src-2.8.10.1/wxPython/setup.py    2009-06-06 14:43:55.000000000 -0400
@@ -882,6 +882,7 @@
                     'wx.tools.Editra',
                     'wx.tools.Editra.src',
                     'wx.tools.Editra.src.autocomp',
+                    'wx.tools.Editra.src.ebmlib',
                     'wx.tools.Editra.src.eclib',
                     'wx.tools.Editra.src.extern',
                     'wx.tools.Editra.src.syntax',
diff -Naur wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/__init__.py wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/__init__.py
--- wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/__init__.py	1969-12-31 19:00:00.000000000 -0500
+++ wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/__init__.py	2009-06-06 03:48:10.000000000 -0400
@@ -0,0 +1,33 @@
+###############################################################################
+# Name: __init__.py                                                           #
+# Purpose: Editra Buisness Model Library                                      #
+# Author: Cody Precord <cprecord@editra.org>                                  #
+# Copyright: (c) 2009 Cody Precord <staff@editra.org>                         #
+# Licence: wxWindows Licence                                                  #
+###############################################################################
+
+"""
+Editra Buisness Model Library:
+
+"""
+
+__author__ = "Cody Precord <cprecord@editra.org>"
+__cvsid__ = "$Id: __init__.py 60840 2009-05-31 16:00:50Z CJP $"
+__revision__ = "$Revision: 60840 $"
+
+#-----------------------------------------------------------------------------#
+
+# Text Utils
+from searcheng import *
+from fchecker import *
+from fileutil import *
+from fileimpl import *
+
+from backupmgr import *
+
+# Storage Classes
+from histcache import *
+from clipboard import *
+
+# Misc
+from miscutil import *
diff -Naur wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/backupmgr.py wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/backupmgr.py
--- wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/backupmgr.py	1969-12-31 19:00:00.000000000 -0500
+++ wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/backupmgr.py	2009-06-06 03:48:10.000000000 -0400
@@ -0,0 +1,160 @@
+###############################################################################
+# Name: backupmgr.py                                                          #
+# Purpose: File Backup Manager                                                #
+# Author: Cody Precord <cprecord@editra.org>                                  #
+# Copyright: (c) 2009 Cody Precord <staff@editra.org>                         #
+# Licence: wxWindows Licence                                                  #
+###############################################################################
+
+"""
+Editra Buisness Model Library: FileBackupMgr
+
+Helper class for managing and creating backups of files.
+
+"""
+
+__author__ = "Cody Precord <cprecord@editra.org>"
+__cvsid__ = "$Id: backupmgr.py 60581 2009-05-10 02:56:00Z CJP $"
+__revision__ = "$Revision: 60581 $"
+
+__all__ = [ 'FileBackupMgr', ]
+
+#-----------------------------------------------------------------------------#
+# Imports
+import os
+import shutil
+
+# Local Imports
+import fileutil
+import fchecker
+
+#-----------------------------------------------------------------------------#
+
+class FileBackupMgr(object):
+    """File backup creator and manager"""
+    def __init__(self, header=None, template=u"%s~"):
+        """Create a BackupManager
+        @keyword header: header to id backups with (Text files only!!)
+        @keyword template: template string for naming backup file with
+
+        """
+        object.__init__(self)
+
+        # Attributes
+        self.checker = fchecker.FileTypeChecker()
+        self.header = header       # Backup id header
+        self.template = template   # Filename template
+
+    def _CheckHeader(self, fname):
+        """Check if the backup file has a header that matches the
+        header used to identify backup files.
+        @param fname: name of file to check
+        @return: bool (True if header is ok, False otherwise)
+
+        """
+        isok = False
+        try:
+            handle = open(fname)
+            line = handle.readline()
+            isok = line.startswith(self.header)
+        except:
+            isok = False
+        finally:
+            handle.close()
+        return isok
+
+    def GetBackupFilename(self, fname):
+        """Get the unique name for the files backup copy
+        @param fname: string (file path)
+        @return: string
+
+        """
+        rname = self.template % fname
+        if self.header is not None and \
+           not self.checker.IsBinary(fname) and \
+           os.path.exists(rname):
+            # Make sure that the template backup name does not match
+            # an existing file that is not a backup file.
+            while not self._CheckHeader(rname):
+                rname = self.template % rname
+                
+        return rname
+
+    def GetBackupWriter(self, fileobj):
+        """Create a backup filewriter method to backup a files contents
+        with.
+        @param fileobj: object implementing fileimpl.FileObjectImpl interface
+        @return: callable(text) to create backup with
+
+        """
+        nfile = fileobj.Clone()
+        fname = self.GetBackupFilename(nfile.GetPath())
+        nfile.SetPath(fname)
+        # Write the header if it is enabled
+        if self.header is not None and not self.checker.IsBinary(fname):
+            nfile.Write(self.header + os.linesep)
+        return nfile.Write
+
+    def HasBackup(self, fname):
+        """Check if a given file has a backup file available or not
+        @param fname: string (file path)
+
+        """
+        backup = self.GetBackupFilename(fname)
+        return os.path.exists(backup)
+
+    def IsBackupNewer(self, fname):
+        """Is the backup of this file newer than the saved version
+        of the file?
+        @param fname: string (file path)
+        @return: bool
+
+        """
+        backup = self.GetBackupFilename(fname)
+        if os.path.exists(fname) and os.path.exists(backup):
+            mod1 = fileutil.GetFileModTime(backup)
+            mod2 = fileutil.GetFileModTime(fname)
+            return mod1 > mod2
+        else:
+            return False
+
+    def MakeBackupCopy(self, fname):
+        """Create a backup copy of the given filename
+        @param fname: string (file path)
+        @return: bool (True == Success)
+
+        """
+        backup = self.GetBackupFilename(fname)
+        try:
+            if os.path.exists(backup):
+                os.remove(backup)
+
+            shutil.copy2(fname, backup)
+        except:
+            return False
+        else:
+            return True
+
+    def MakeBackupCopyAsync(self, fname):
+        """Do the backup asyncronously
+        @param fname: string (file path)
+        @todo: Not implemented yet
+
+        """
+        raise NotImplementedError("TODO: implement once threadpool is finished")
+
+    def SetBackupFileTemplate(self, tstr):
+        """Set the filename template for generating the backupfile name
+        @param tstr: template string i.e) %s~
+
+        """
+        assert tstr.count("%s") == 1, "Format statment must only have one arg"
+        self.template = tstr
+
+    def SetHeader(self, header):
+        """Set the header string for identifying a file as a backup
+        @param header: string (single line only)
+
+        """
+        assert '\n' not in header, "Header must only be a single line"
+        self.header = header
diff -Naur wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/clipboard.py wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/clipboard.py
--- wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/clipboard.py	1969-12-31 19:00:00.000000000 -0500
+++ wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/clipboard.py	2009-06-06 03:48:10.000000000 -0400
@@ -0,0 +1,135 @@
+###############################################################################
+# Name: histcache.py                                                          #
+# Purpose: History Cache                                                      #
+# Author: Cody Precord <cprecord@editra.org>                                  #
+# Copyright: (c) 2009 Cody Precord <staff@editra.org>                         #
+# Licence: wxWindows Licence                                                  #
+###############################################################################
+
+"""
+Editra Buisness Model Library: Clipboard
+
+Clipboard helper class
+
+"""
+
+__author__ = "Hasan Aljudy"
+__cvsid__ = "$Id: clipboard.py 60681 2009-05-17 10:41:42Z CJP $"
+__revision__ = "$Revision: 60681 $"
+
+__all__ = [ 'Clipboard',]
+
+#-----------------------------------------------------------------------------#
+# Imports
+import wx
+
+#-----------------------------------------------------------------------------#
+
+class Clipboard(object):
+    """Multiple clipboards as named registers (as per vim)
+
+    " is an alias for system clipboard and is also the default clipboard.
+
+    @note: The only way to access multiple clipboards right now is through
+           Normal mode when Vi(m) emulation is enabled.
+
+    """
+    NAMES = list(u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_')
+    registers = {}
+    current = u'"'
+
+    @classmethod
+    def Switch(cls, reg):
+        """Switch to register
+        @param reg: char
+
+        """
+        if reg in cls.NAMES or reg == u'"':
+            cls.current = reg
+        else:
+            raise Exception(u"Switched to invalid register name")
+
+    @classmethod
+    def NextFree(cls, reg):
+        """Switch to the next free register. If current register is free, no
+        switching happens.
+
+        A free register is one that's either unused or has no content
+
+        @param reg: char
+        @note: This is not used yet.
+
+        """
+        if cls.Get() == u'':
+            return
+
+        for name in cls.NAMES:
+            if cls.registers.get(name, u'') == u'':
+                cls.Switch(name)
+                break
+
+    @classmethod
+    def AllUsed(cls):
+        """Get a dictionary mapping all used clipboards (plus the system
+        clipboard) to their content.
+
+        @note: This is not used yet.
+
+        """
+        cmd_map = { u'"': cls.SystemGet() }
+        for name in cls.NAMES:
+            if cls.registers.get(name, u''):
+                cmd_map[name] = cls.registers[name]
+        return cmd_map
+
+    @classmethod
+    def Get(cls):
+        """Get the content of the current register. Used for pasting"""
+        if cls.current == u'"':
+            return cls.SystemGet()
+        else:
+            return cls.registers.get( cls.current, u'' )
+
+    @classmethod
+    def Set(cls, text):
+        """Set the content of the current register
+        @param text: string
+
+        """
+        if cls.current == u'"':
+            return cls.SystemSet(text)
+        else:
+            cls.registers[cls.current] = text
+
+    @classmethod
+    def SystemGet(cls):
+        """Get text from the system clipboard
+        @return: string
+
+        """
+        text = None
+        if wx.TheClipboard.Open():
+            if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)):
+                text = wx.TextDataObject()
+                wx.TheClipboard.GetData(text)
+
+            wx.TheClipboard.Close()
+
+        if text is not None:
+            return text.GetText()
+        else:
+            return u''
+
+    @classmethod
+    def SystemSet(cls, text):
+        """Set text into the system clipboard
+        @param text: string
+        @return: bool
+
+        """
+        ok = False
+        if wx.TheClipboard.Open():
+            wx.TheClipboard.SetData(wx.TextDataObject(text))
+            wx.TheClipboard.Close()
+            ok = True
+        return ok
\ No newline at end of file
diff -Naur wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/fchecker.py wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/fchecker.py
--- wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/fchecker.py	1969-12-31 19:00:00.000000000 -0500
+++ wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/fchecker.py	2009-06-06 03:48:10.000000000 -0400
@@ -0,0 +1,82 @@
+###############################################################################
+# Name: fchecker.py                                                           #
+# Purpose: Filetype checker object.                                           #
+# Author: Cody Precord <cprecord@editra.org>                                  #
+# Copyright: (c) 2009 Cody Precord <staff@editra.org>                         #
+# Licence: wxWindows Licence                                                  #
+###############################################################################
+
+"""
+Editra Buisness Model Library: FileTypeChecker
+
+Helper class for checking what kind of a content a file contains.
+
+"""
+
+__author__ = "Cody Precord <cprecord@editra.org>"
+__cvsid__ = "$Id: fchecker.py 60505 2009-05-03 19:18:21Z CJP $"
+__revision__ = "$Revision: 60505 $"
+
+__all__ = [ 'FileTypeChecker', ]
+
+#-----------------------------------------------------------------------------#
+# Imports
+import os
+
+#-----------------------------------------------------------------------------#
+
+class FileTypeChecker(object):
+    """File type checker and recognizer"""
+    TXTCHARS = ''.join(map(chr, [7, 8, 9, 10, 12, 13, 27] + range(0x20, 0x100)))
+    ALLBYTES = ''.join(map(chr, range(256)))
+
+    def __init__(self, preread=4096):
+        """Create the FileTypeChecker
+        @keyword preread: number of bytes to read for checking file type
+
+        """
+        object.__init__(self)
+
+        # Attributes
+        self._preread = preread
+
+    @staticmethod
+    def _GetHandle(fname):
+        """Get a file handle for reading
+        @param fname: filename
+        @return: file object or None
+
+        """
+        try:
+            handle = open(fname, 'rb')
+        except:
+            handle = None
+        return handle
+
+    def IsBinary(self, fname):
+        """Is the file made up of binary data
+        @param fname: filename to check
+        @return: bool
+
+        """
+        handle = self._GetHandle(fname)
+        if handle is not None:
+            bytes = handle.read(self._preread)
+            handle.close()
+            nontext = bytes.translate(FileTypeChecker.ALLBYTES,
+                                      FileTypeChecker.TXTCHARS)
+            return bool(nontext)
+        else:
+            return False
+
+    def IsReadableText(self, fname):
+        """Is the given path readable as text. Will return True if the
+        file is accessable by current user and is plain text.
+        @param fname: filename
+        @return: bool
+
+        """
+        f_ok = False
+        if os.access(fname, os.R_OK):
+            f_ok = not self.IsBinary(fname)
+        return f_ok
diff -Naur wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/fileimpl.py wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/fileimpl.py
--- wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/fileimpl.py	1969-12-31 19:00:00.000000000 -0500
+++ wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/fileimpl.py	2009-06-06 03:48:10.000000000 -0400
@@ -0,0 +1,215 @@
+###############################################################################
+# Name: Cody Precord                                                          #
+# Purpose: File Object Interface Implementation                               #
+# Author: Cody Precord <cprecord@editra.org>                                  #
+# Copyright: (c) 2009 Cody Precord <staff@editra.org>                         #
+# License: wxWindows License                                                  #
+###############################################################################
+
+"""
+Editra Buisness Model Library: FileObjectImpl
+
+Implementation of a file object interface class. Objects and methods inside
+of this library expect a file object that derives from this interface.
+
+"""
+
+__author__ = "Cody Precord <cprecord@editra.org>"
+__svnid__ = "$Id: fileimpl.py 60582 2009-05-10 04:24:30Z CJP $"
+__revision__ = "$Revision: 60582 $"
+
+#--------------------------------------------------------------------------#
+# Imports
+import os
+
+# Editra Buisness Model Imports
+import fileutil
+
+#--------------------------------------------------------------------------#
+
+class FileObjectImpl(object):
+    """File Object Interface implementation base class"""
+    def __init__(self, path=u'', modtime=0):
+        object.__init__(self)
+
+        # Attributes
+        self._path = path
+        self._modtime = modtime
+
+        self._handle = None
+        self.open = False
+
+        self.last_err = None
+
+    def ClearLastError(self):
+        """Reset the error marker on this file"""
+        del self.last_err
+        self.last_err = None
+
+    def Clone(self):
+        """Clone the file object
+        @return: FileObject
+
+        """
+        fileobj = FileObjectImpl(self._path, self._modtime)
+        fileobj.SetLastError(self.last_err)
+        return fileobj
+
+    def Close(self):
+        """Close the file handle
+        @note: this is normally done automatically after a read/write operation
+
+        """
+        try:
+            self._handle.close()
+        except:
+            pass
+
+        self.open = False
+
+    def DoOpen(self, mode):
+        """Opens and creates the internal file object
+        @param mode: mode to open file in
+        @return: True if opened, False if not
+        @postcondition: self._handle is set to the open handle
+
+        """
+        if not len(self._path):
+            return False
+
+        try:
+            file_h = open(self._path, mode)
+        except (IOError, OSError), msg:
+            self.last_err = msg
+            return False
+        else:
+            self._handle = file_h
+            self.open = True
+            return True
+
+    def GetExtension(self):
+        """Get the files extension if it has one else simply return the
+        filename minus the path.
+        @return: string file extension (no dot)
+
+        """
+        fname = os.path.split(self._path)
+        return fname[-1].split(os.extsep)[-1].lower()
+
+    def GetHandle(self):
+        """Get this files handle"""
+        return self._handle
+
+    def GetLastError(self):
+        """Return the last error that occured when using this file
+        @return: err traceback or None
+
+        """
+        return unicode(self.last_err).replace("u'", "'")
+
+    def GetModtime(self):
+        """Get the timestamp of this files last modification"""
+        return self._modtime
+
+    def GetPath(self):
+        """Get the path of the file
+        @return: string
+
+        """
+        return self._path
+
+    def GetSize(self):
+        """Get the size of the file
+        @return: int
+
+        """
+        if self._path:
+            return fileutil.GetFileSize(self._path)
+        else:
+            return 0
+
+    @property
+    def Handle(self):
+        """Raw file handle property"""
+        return self._handle
+
+    def IsOpen(self):
+        """Check if file is open or not
+        @return: bool
+
+        """
+        return self.open
+
+    def IsReadOnly(self):
+        """Is the file Read Only
+        @return: bool
+
+        """
+        if os.path.exists(self._path):
+            return not os.access(self._path, os.R_OK|os.W_OK)
+        else:
+            return False
+
+    @property
+    def Modtime(self):
+        """File modification time propery"""
+        return self.GetModtime()
+
+    @property
+    def ReadOnly(self):
+        """Is the file read only?"""
+        return self.IsReadOnly()
+
+    def ResetAll(self):
+        """Reset all file attributes"""
+        self._handle = None
+        self.open = False
+        self._path = u''
+        self._modtime = 0
+        self.last_err = None
+
+    def SetLastError(self, err):
+        """Set the last error
+        @param err: exception object / msg
+
+        """
+        self.last_err = err
+
+    def SetPath(self, path):
+        """Set the path of the file
+        @param path: absolute path to file
+
+        """
+        self._path = path
+
+    def SetModTime(self, mtime):
+        """Set the modtime of this file
+        @param mtime: long int to set modtime to
+
+        """
+        self._modtime = mtime
+
+    #--- SHould be overridden by subclass ---#
+
+    def Read(self):
+        """Open/Read the file
+        @return: string (file contents)
+
+        """
+        txt = u''
+        if self.DoOpen('rb'):
+            try:
+                txt = self._handle.read()
+            except:
+                pass
+
+        return txt
+    
+    def Write(self, value):
+        """Open/Write the value to disk
+        @param value: string
+
+        """
+        if self.DoOpen('wb'):
+            self._handle.write(value)
+            self._handle.close()
diff -Naur wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/fileutil.py wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/fileutil.py
--- wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/fileutil.py	1969-12-31 19:00:00.000000000 -0500
+++ wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/fileutil.py	2009-06-06 03:48:10.000000000 -0400
@@ -0,0 +1,180 @@
+###############################################################################
+# Name: fileutil.py                                                           #
+# Purpose: File Management Utilities.                                         #
+# Author: Cody Precord <cprecord@editra.org>                                  #
+# Copyright: (c) 2009 Cody Precord <staff@editra.org>                         #
+# Licence: wxWindows Licence                                                  #
+###############################################################################
+
+"""
+Editra Buisness Model Library: File Utilities
+
+Utility functions for managing and working with files.
+
+"""
+
+__author__ = "Cody Precord <cprecord@editra.org>"
+__svnid__ = "$Id: fileutil.py 60523 2009-05-05 18:49:31Z CJP $"
+__revision__ = "$Revision: 60523 $"
+
+__all__ = [ 'GetFileModTime', 'GetFileSize', 'GetUniqueName', 'MakeNewFile',
+            'MakeNewFolder', 'GetFileExtension', 'GetFileName', 'GetPathName',
+            'ResolveRealPath', 'IsLink' ]
+
+#-----------------------------------------------------------------------------#
+# Imports
+import os
+import platform
+import stat
+
+UNIX = WIN = False
+if platform.system().lower() in ['windows', 'microsoft']:
+    WIN = True
+    try:
+        # Check for if win32 extensions are available
+        import win32com.client as win32client
+    except ImportError:
+        win32client = None
+else:
+    UNIX = True
+
+#-----------------------------------------------------------------------------#
+
+def GetFileExtension(file_str):
+    """Gets last atom at end of string as extension if
+    no extension whole string is returned
+    @param file_str: path or file name to get extension from
+
+    """
+    return file_str.split('.')[-1]
+
+def GetFileModTime(file_name):
+    """Returns the time that the given file was last modified on
+    @param file_name: path of file to get mtime of
+
+    """
+    try:
+        mod_time = os.path.getmtime(file_name)
+    except (OSError, EnvironmentError):
+        mod_time = 0
+    return mod_time
+
+def GetFileName(path):
+    """Gets last atom on end of string as filename
+    @param path: full path to get filename from
+
+    """
+    return os.path.split(path)[-1]
+
+def GetFileSize(path):
+    """Get the size of the file at a given path
+    @param path: Path to file
+    @return: long
+
+    """
+    try:
+        return os.stat(path)[stat.ST_SIZE]
+    except:
+        return 0
+
+def GetPathName(path):
+    """Gets the path minus filename
+    @param path: full path to get base of
+
+    """
+    return os.path.split(path)[0]
+
+def IsLink(path):
+    """Is the file a link
+    @return: bool
+
+    """
+    if WIN:
+        return path.endswith(".lnk") or os.path.islink(path)
+    else:
+        return os.path.islink(path)
+
+def ResolveRealPath(link):
+    """Return the real path of the link file
+    @param link: path of link file
+    @return: string
+
+    """
+    assert IsLink(link), "ResolveRealPath expects a link file!"
+    realpath = link
+    if WIN and win32client is not None:
+        shell = win32client.Dispatch("WScript.Shell")
+        shortcut = shell.CreateShortCut(link)
+        realpath = shortcut.Targetpath
+    else:
+        realpath = os.path.realpath(link)
+    return realpath
+
+#-----------------------------------------------------------------------------#
+
+def GetUniqueName(path, name):
+    """Make a file name that will be unique in case a file of the
+    same name already exists at that path.
+    @param path: Root path to folder of files destination
+    @param name: desired file name base
+    @return: string
+
+    """
+    tmpname = os.path.join(path, name)
+    if os.path.exists(tmpname):
+        if '.' not in name:
+            ext = ''
+            fbase = name
+        else:
+            ext = '.' + name.split('.')[-1]
+            fbase = name[:-1 * len(ext)]
+
+        inc = len([x for x in os.listdir(path) if x.startswith(fbase)])
+        tmpname = os.path.join(path, "%s-%d%s" % (fbase, inc, ext))
+        while os.path.exists(tmpname):
+            inc = inc + 1
+            tmpname = os.path.join(path, "%s-%d%s" % (fbase, inc, ext))
+
+    return tmpname
+
+
+#-----------------------------------------------------------------------------#
+
+def MakeNewFile(path, name):
+    """Make a new file at the given path with the given name.
+    If the file already exists, the given name will be changed to
+    a unique name in the form of name + -NUMBER + .extension
+    @param path: path to directory to create file in
+    @param name: desired name of file
+    @return: Tuple of (success?, Path of new file OR Error message)
+
+    """
+    if not os.path.isdir(path):
+        path = os.path.dirname(path)
+    fname = GetUniqueName(path, name)
+
+    try:
+        open(fname, 'w').close()
+    except (IOError, OSError), msg:
+        return (False, str(msg))
+
+    return (True, fname)
+
+def MakeNewFolder(path, name):
+    """Make a new folder at the given path with the given name.
+    If the folder already exists, the given name will be changed to
+    a unique name in the form of name + -NUMBER.
+    @param path: path to create folder on
+    @param name: desired name for folder
+    @return: Tuple of (success?, new dirname OR Error message)
+
+    """
+    if not os.path.isdir(path):
+        path = os.path.dirname(path)
+    folder = GetUniqueName(path, name)
+    try:
+        os.mkdir(folder)
+    except (OSError, IOError), msg:
+        return (False, str(msg))
+
+    return (True, folder)
diff -Naur wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/histcache.py wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/histcache.py
--- wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/histcache.py	1969-12-31 19:00:00.000000000 -0500
+++ wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/histcache.py	2009-06-06 03:48:10.000000000 -0400
@@ -0,0 +1,131 @@
+###############################################################################
+# Name: histcache.py                                                          #
+# Purpose: History Cache                                                      #
+# Author: Cody Precord <cprecord@editra.org>                                  #
+# Copyright: (c) 2009 Cody Precord <staff@editra.org>                         #
+# Licence: wxWindows Licence                                                  #
+###############################################################################
+
+"""
+Editra Buisness Model Library: HistoryCache
+
+History cache that acts as a stack for managing a history list o
+
+"""
+
+__author__ = "Cody Precord <cprecord@editra.org>"
+__cvsid__ = "$Id: histcache.py 60613 2009-05-13 02:27:21Z CJP $"
+__revision__ = "$Revision: 60613 $"
+
+__all__ = [ 'HistoryCache', 'HIST_CACHE_UNLIMITED']
+
+#-----------------------------------------------------------------------------#
+# Imports
+
+#-----------------------------------------------------------------------------#
+# Globals
+HIST_CACHE_UNLIMITED = -1
+
+#-----------------------------------------------------------------------------#
+
+class HistoryCache(object):
+    def __init__(self, max_size=HIST_CACHE_UNLIMITED):
+        object.__init__(self)
+
+        # Attributes
+        self._list = list()
+        self.cpos = -1
+        self.max_size = max_size
+
+    def _Resize(self):
+        """Adjust cache size based on max size setting"""
+        if self.max_size != HIST_CACHE_UNLIMITED:
+            lsize = len(self._list)
+            if lsize:
+                adj = self.max_size - lsize 
+                if adj < 0:
+                    self._list.pop(0)
+                    self.cpos = len(self._list) - 1 
+
+    def Clear(self):
+        """Clear the history cache"""
+        del self._list
+        self._list = list()
+        self.cpos = -1
+
+    def GetSize(self):
+        """Get the current size of the cache
+        @return: int (number of items in the cache)
+
+        """
+        return len(self._list)
+
+    def GetMaxSize(self):
+        """Get the max size of the cache
+        @return: int
+
+        """
+        return self.max_size
+
+    def GetNextItem(self):
+        """Get the next item in the history cache, moving the
+        current postion towards the end of the cache.
+        @return: object or None if at end of list
+
+        """
+        item = None
+        if self.cpos < len(self._list) - 1:
+            self.cpos += 1
+            item = self._list[self.cpos]
+        return item
+
+    def GetPreviousItem(self):
+        """Get the previous item in the history cache, moving the
+        current postion towards the begining of the cache.
+        @return: object or None if at start of list
+
+        """
+        item = None
+        if self.cpos >= 0:
+            item = self._list[self.cpos]
+            self.cpos -= 1
+        return item
+
+    def HasPrevious(self):
+        """Are there more items to the left of the current position
+        @return: bool
+
+        """
+        more = self.cpos >= 0
+        return more
+
+    def HasNext(self):
+        """Are there more items to the right of the current position
+        @return: bool
+
+        """
+        if self.cpos == -1 and len(self._list):
+            more = True
+        else:
+            more = self.cpos >= 0 and self.cpos < len(self._list)
+        return more
+
+    def PutItem(self, item):
+        """Put an item on the top of the cache
+        @param item: object
+
+        """
+        if self.cpos != len(self._list) - 1:
+            self._list = self._list[:self.cpos]
+        self._list.append(item)
+        self.cpos += 1
+        self._Resize()
+
+    def SetMaxSize(self, max_size):
+        """Set the maximum size of the cache
+        @param max_size: int (HIST_CACHE_UNLIMITED for unlimited size)
+
+        """
+        assert max_size > 0 or max_size == 1, "Invalid max size"
+        self.max_size = max_size
+        self._Resize()
diff -Naur wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/miscutil.py wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/miscutil.py
--- wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/miscutil.py	1969-12-31 19:00:00.000000000 -0500
+++ wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/miscutil.py	2009-06-06 03:48:10.000000000 -0400
@@ -0,0 +1,33 @@
+###############################################################################
+# Name: miscutil.py                                                           #
+# Purpose: Various helper functions.                                          #
+# Author: Cody Precord <cprecord@editra.org>                                  #
+# Copyright: (c) 2009 Cody Precord <staff@editra.org>                         #
+# Licence: wxWindows Licence                                                  #
+###############################################################################
+
+"""
+Editra Buisness Model Library: MiscUtil
+
+Various helper functions
+
+"""
+
+__author__ = "Cody Precord <cprecord@editra.org>"
+__cvsid__ = "$Id: miscutil.py 60840 2009-05-31 16:00:50Z CJP $"
+__revision__ = "$Revision: 60840 $"
+
+__all__ = [ 'MinMax', ]
+
+#-----------------------------------------------------------------------------#
+# Imports
+
+#-----------------------------------------------------------------------------#
+
+def MinMax(arg1, arg2):
+    """Return an ordered tuple of the minumum and maximum value
+    of the two args.
+    @return: tuple
+
+    """
+    return min(arg1, arg2), max(arg1, arg2)
diff -Naur wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/searcheng.py wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/searcheng.py
--- wxPython-src-2.8.10.1-orig/wxPython/wx/tools/Editra/src/ebmlib/searcheng.py	1969-12-31 19:00:00.000000000 -0500
+++ wxPython-src-2.8.10.1/wxPython/wx/tools/Editra/src/ebmlib/searcheng.py	2009-06-06 03:48:10.000000000 -0400
@@ -0,0 +1,400 @@
+###############################################################################
+# Name: searcheng.py                                                          #
+# Purpose: Text search engine and utilities                                   #
+# Author: Cody Precord <cprecord@editra.org>                                  #
+# Copyright: (c) 2009 Cody Precord <staff@editra.org>                         #
+# Licence: wxWindows Licence                                                  #
+###############################################################################
+
+"""
+Editra Buisness Model Library: SearchEngine
+
+Text Search Engine for finding text and grepping files
+
+"""
+
+__author__ = "Cody Precord <cprecord@editra.org>"
+__cvsid__ = "$Id: searcheng.py 60680 2009-05-17 20:31:58Z CJP $"
+__revision__ = "$Revision: 60680 $"
+
+__all__ = [ 'SearchEngine', ]
+
+#-----------------------------------------------------------------------------#
+# Imports
+import os
+import re
+import fnmatch
+import types
+from StringIO import StringIO
+
+# Local imports
+import fchecker
+
+#-----------------------------------------------------------------------------#
+
+class SearchEngine(object):
+    """Text Search Engine
+    All Search* methods are iterable generators
+    All Find* methods do a complete search and return the match collection
+    @summary: Text Search Engine
+    @todo: Add file filter support
+
+    """
+    def __init__(self, query, regex=True, down=True,
+                  matchcase=True, wholeword=False):
+        """Initialize a search engine object
+        @param query: search string
+        @keyword regex: Is a regex search
+        @keyword down: Search down or up
+        @keyword matchcase: Match case
+        @keyword wholeword: Match whole word
+
+        """
+        object.__init__(self)
+
+        # Attributes
+        self._isregex = regex
+        self._next = down
+        self._matchcase = matchcase
+        self._wholeword = wholeword
+        self._unicode = False
+        self._query = query
+        self._regex = u''
+        self._pool = u''
+        self._lmatch = None             # Last match object
+        self._filters = None            # File Filters
+        self._formatter = lambda f, l, m: u"%s %d: %s" % (f, l+1, m)
+        self._CompileRegex()
+
+    def _CompileRegex(self):
+        """Prepare and compile the regex object based on the current state
+        and settings of the engine.
+        @postcondition: the engines regular expression is created
+
+        """
+        tmp = self._query
+        if not self._isregex:
+            tmp = re.escape(tmp)
+
+        if self._wholeword:
+            tmp = "\\b%s\\b" % tmp
+
+        flags = re.MULTILINE
+
+        if not self._matchcase:
+            flags |= re.IGNORECASE
+
+        if self._unicode:
+            flags |= re.UNICODE
+
+        try:
+            self._regex = re.compile(tmp, flags)
+        except:
+            self._regex = None
+
+    def ClearPool(self):
+        """Clear the search pool"""
+        del self._pool
+        self._pool = u""
+
+    def Find(self, spos=0):
+        """Find the next match based on the state of the search engine
+        @keyword spos: search start position
+        @return: tuple (match start pos, match end pos) or None if no match
+        @note: L{SetSearchPool} has been called to set search string
+
+        """
+        if self._regex is None:
+            return None
+
+        if self._next:
+            return self.FindNext(spos)
+        else:
+            if spos == 0:
+                spos = -1
+            return self.FindPrev(spos)
+
+    def FindAll(self):
+        """Find all the matches in the current context
+        @return: list of tuples [(start1, end1), (start2, end2), ]
+
+        """
+        if self._regex is None:
+            return list()
+
+        matches = [match for match in self._regex.finditer(self._pool)]
+        return matches
+
+    def FindAllLines(self):
+        """Find all the matches in the current context
+        @return: list of strings
+
+        """
+        rlist = list()
+        if self._regex is None:
+            return rlist
+
+        for lnum, line in enumerate(StringIO(self._pool)):
+            if self._regex.search(line) is not None:
+                rlist.append(self._formatter(u"Untitled", lnum, line))
+
+        return rlist
+
+    def FindNext(self, spos=0):
+        """Find the next match of the query starting at spos
+        @keyword spos: search start position in string
+        @return: tuple (match start pos, match end pos) or None if no match
+        @note: L{SetSearchPool} has been called to set the string to search in.
+
+        """
+        if self._regex is None:
+            return None
+
+        if spos < len(self._pool):
+            match = self._regex.search(self._pool[spos:])
+            if match is not None:
+                self._lmatch = match
+                return match.span()
+        return None
+
+    def FindPrev(self, spos=-1):
+        """Find the previous match of the query starting at spos
+        @keyword spos: search start position in string
+        @return: tuple (match start pos, match end pos)
+
+        """
+        if self._regex is None:
+            return None
+
+        if spos+1 < len(self._pool):
+            matches = [match for match in
+                       self._regex.finditer(self._pool[:spos])]
+            if len(matches):
+                lmatch = matches[-1]
+                self._lmatch = lmatch
+                return (lmatch.start(), lmatch.end())
+        return None
+
+    def GetLastMatch(self):
+        """Get the last found match object from the previous L{FindNext} or
+        L{FindPrev} action.
+        @return: match object or None
+
+        """
+        return self._lmatch
+
+    def GetOptionsString(self):
+        """Get a string describing the search engines options"""
+        rstring = u"\"%s\" [ " % self._query
+        for desc, attr in (("regex: %s", self._isregex),
+                           ("match case: %s", self._matchcase),
+                           ("whole word: %s", self._wholeword)):
+            if attr:
+                rstring += (desc % u"on; ")
+            else:
+                rstring += (desc % u"off; ")
+        rstring += u"]"
+
+        return rstring
+
+    def GetQuery(self):
+        """Get the raw query string used by the search engine
+        @return: string
+
+        """
+        return self._query
+
+    def GetQueryObject(self):
+        """Get the regex object used for the search. Will return None if
+        there was an error in creating the object.
+        @return: pattern object
+
+        """
+        return self._regex
+
+    def GetSearchPool(self):
+        """Get the search pool string for this L{SearchEngine}.
+        @return: string
+
+        """
+        return self._pool
+
+    def IsMatchCase(self):
+        """Is the engine set to a case sensitive search
+        @return: bool
+
+        """
+        return self._matchcase
+
+    def IsRegEx(self):
+        """Is the engine searching with the query as a regular expression
+        @return: bool
+
+        """
+        return self._isregex
+
+    def IsWholeWord(self):
+        """Is the engine set to search for wholeword matches
+        @return: bool
+
+        """
+        return self._wholeword
+
+    def SearchInBuffer(self, sbuffer):
+        """Search in the buffer
+        @param sbuffer: buffer like object
+        @todo: implement
+
+        """
+        raise NotImplementedError
+
+    def SearchInDirectory(self, directory, recursive=True):
+        """Search in all the files found in the given directory
+        @param directory: directory path
+        @keyword recursive: search recursivly
+
+        """
+        if self._regex is None:
+            return
+
+        # Get all files in the directories
+        paths = [os.path.join(directory, fname)
+                for fname in os.listdir(directory) if not fname.startswith('.')]
+
+        # Filter out files that don't match the current filter(s)
+        if self._filters is not None and len(self._filters):
+            filtered = list()
+            for fname in paths:
+                if os.path.isdir(fname):
+                    filtered.append(fname)
+                    continue
+
+                for pat in self._filters:
+                    if fnmatch.fnmatch(fname, pat):
+                        filtered.append(fname)
+            paths = filtered
+
+        # Begin searching in the paths
+        for path in paths:
+            if recursive and os.path.isdir(path):
+                # Recursive call to decend into directories
+                for match in self.SearchInDirectory(path, recursive):
+                    yield match
+            else:
+                for match in self.SearchInFile(path):
+                    yield match
+        return
+
+    def SearchInFile(self, fname):
+        """Search in a file for all lines with matches of the set query and
+        yield the results as they are found.
+        @param fname: filename
+        @todo: unicode handling
+
+        """
+        if self._regex is None:
+            return
+
+        checker = fchecker.FileTypeChecker()
+        if checker.IsReadableText(fname):
+            try:
+                fobj = open(fname, 'rb')
+            except (IOError, OSError):
+                return
+            else:
+                # Special token to signify start of a search
+                yield (None, fname)
+
+            for lnum, line in enumerate(fobj):
+                if self._regex.search(line) is not None:
+                    yield self._formatter(fname, lnum, line)
+            fobj.close()
+        return
+
+    def SearchInFiles(self, flist):
+        """Search in a list of files and yield results as they are found.
+        @param flist: list of file names
+
+        """
+        if self._regex is None:
+            return
+
+        for fname in flist:
+            for match in self.SearchInFile(fname):
+                yield match
+        return
+
+    def SearchInString(self, sstring, startpos=0):
+        """Search in a string
+        @param sstring: string to search in
+        @keyword startpos: search start position
+
+        """
+        raise NotImplementedError
+
+    def SetFileFilters(self, filters):
+        """Set the file filters to specify what type of files to search in
+        the filter should be a list of wild card patterns to match.
+        @param filters: list of strings ['*.py', '*.pyw']
+
+        """
+        self._filters = filters
+
+    def SetFlags(self, isregex=None, matchcase=None, wholeword=None, down=None):
+        """Set the search engine flags. Leaving the parameter set to None
+        will not change the flag. Setting it to non None will change the value.
+        @keyword isregex: is regex search
+        @keyword matchcase: matchcase search
+        @keyword wholeword: wholeword search
+        @keyword down: search down or up
+
+        """
+        for attr, val in (('_isregex', isregex), ('_matchcase', matchcase),
+                          ('_wholeword', wholeword), ('_next', down)):
+            if val is not None:
+                setattr(self, attr, val)
+        self._CompileRegex()
+
+    def SetMatchCase(self, case=True):
+        """Set whether the engine will use case sensative searches
+        @keyword case: bool
+
+        """
+        self._matchcase = case
+        self._CompileRegex()
+
+    def SetResultFormatter(self, funct):
+        """Set the result formatter function
+        @param funct: callable(filename, linenum, matchstr)
+
+        """
+        assert callable(funct)
+        self._formatter = funct
+
+    def SetSearchPool(self, pool):
+        """Set the search pool used by the Find methods
+        @param pool: string to search in
+
+        """
+        del self._pool
+        self._pool = pool
+        if isinstance(self._pool, types.UnicodeType):
+            self._unicode = True
+            self._CompileRegex()
+
+    def SetQuery(self, query):
+        """Set the search query
+        @param query: string
+
+        """
+        self._query = query
+        self._CompileRegex()
+
+    def SetUseRegex(self, use=True):
+        """Set whether the engine is using regular expresion searches or
+        not.
+        @keyword use: bool
+
+        """
+        self._isregex = use
+        self._CompileRegex()