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 # +# Copyright: (c) 2009 Cody Precord # +# Licence: wxWindows Licence # +############################################################################### + +""" +Editra Buisness Model Library: + +""" + +__author__ = "Cody Precord " +__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 # +# Copyright: (c) 2009 Cody Precord # +# Licence: wxWindows Licence # +############################################################################### + +""" +Editra Buisness Model Library: FileBackupMgr + +Helper class for managing and creating backups of files. + +""" + +__author__ = "Cody Precord " +__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 # +# Copyright: (c) 2009 Cody Precord # +# 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 # +# Copyright: (c) 2009 Cody Precord # +# Licence: wxWindows Licence # +############################################################################### + +""" +Editra Buisness Model Library: FileTypeChecker + +Helper class for checking what kind of a content a file contains. + +""" + +__author__ = "Cody Precord " +__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 # +# Copyright: (c) 2009 Cody Precord # +# 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 " +__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 # +# Copyright: (c) 2009 Cody Precord # +# Licence: wxWindows Licence # +############################################################################### + +""" +Editra Buisness Model Library: File Utilities + +Utility functions for managing and working with files. + +""" + +__author__ = "Cody Precord " +__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 # +# Copyright: (c) 2009 Cody Precord # +# Licence: wxWindows Licence # +############################################################################### + +""" +Editra Buisness Model Library: HistoryCache + +History cache that acts as a stack for managing a history list o + +""" + +__author__ = "Cody Precord " +__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 # +# Copyright: (c) 2009 Cody Precord # +# Licence: wxWindows Licence # +############################################################################### + +""" +Editra Buisness Model Library: MiscUtil + +Various helper functions + +""" + +__author__ = "Cody Precord " +__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 # +# Copyright: (c) 2009 Cody Precord # +# Licence: wxWindows Licence # +############################################################################### + +""" +Editra Buisness Model Library: SearchEngine + +Text Search Engine for finding text and grepping files + +""" + +__author__ = "Cody Precord " +__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()