diff options
Diffstat (limited to 'lib/python2.7/site-packages/wx-3.0-msw/wx/py')
34 files changed, 11675 insertions, 0 deletions
diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/CHANGES.txt b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/CHANGES.txt new file mode 100644 index 0000000..501c091 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/CHANGES.txt @@ -0,0 +1,796 @@ +0.9.7.9 (9/8/09) +------------------- +Finalized the naming convention so that references to shell in the new "slices" +shell are renamed to "sliceshell" (likewise, Shell becomes SlicesShell) + +Also, renamed the file "slices.py" to "sliceshell.py". +Since my goal is to keep PySlices.py as the equivalent to PyCrust.py, + I think this new convention makes the most sense... + +Now (Finally): + shell.py contains the classes: Shell, ShellFacade, and ShellFrame + sliceshell.py contains the classes: SlicesShell, SlicesShellFacade, + and SlicesShellFrame + crust.py contains: Crust and CrustFrame + crustslices.py contains: CrustSlices and CrustSlicesFrame + PyShell.py and PySlicesShell.py are the respective standalone shell apps + PyCrust.py and PySlices.py are the respective apps that also include Filling + + frame.py etc. still continue to service both PyCrust and PySlices. + +0.9.7.8 (9/8/09) +------------------- +Added open/save abilities to PySlices (PyCrust remains the same). + This uses a special format with extension .pyslices. + A file, *.pyslices will contain a text header and + also a line denoting the beginning of each new slice, + naming the type of slice as well (grouping, input, output) + All output is commented with a single '#' which is removed upon loading. + This ensures that a well contstucted .pyslices file is also + a valid python script! + +Added the ability to load an entire python file into a new Input slice + with Ctrl-L +Added the ability to load pyslices files from the command line. + +Split the functionality of crust.py (functioning for both PyCrust and PySlices) + into crust.py (only PyCrust) and crustslices.py (only for PySlices). + +After revising the naming conventions: + shell.py contains the classes: Shell, ShellFacade, and ShellFrame + slice.py contains the classes: Shell, ShellFacade, and ShellFrame + crust.py contains: Crust and CrustFrame + crustslices.py contains: CrustSlices and CrustSlicesFrame + PyShell.py and PySlicesShell.py are the respective standalone shell apps + PyCrust.py and PySlices.py are the respective apps that also include Filling + + frame.py etc. still continue to service both PyCrust and PySlices. + +0.9.7.7 (8/25/2009) (Current--Still Need to SVNAdd the new file PySlicesShell.py) +------------------- +Added code to introspect.py to check for Enthought's "Traits" attributes. +Added PySlicesShell.py. PySlices shell is PySlices without Filling + and other crust attributes. +Fixed a bug with Output_BG + +0.9.7.6 (7/18/2009) +------------------- +Made output have a slight-blue background +Added a tutorial that can be disabled in Options->Startup. +Added "Shell Mode" which uses >>> and ... markers for input slices and uses two returns for command execution +Changed manual completion keybindings. +Cleaned up keybinding help. +Made Items in Options->Startup menu automatically save to the config file (since they don't affect anything until startup) +Major code cleanup, removal of much dead code, shortening of very long lines, etc... + +0.9.6.9 (7/9/2009) +------------------- +Fixed Undo marker issues and a bug in selection overwrite. + +0.9.6.8 (7/1/2009) +------------------- +Merged changes with SVN trunk. + +0.9.6.4 thru 0.9.6.6 (10/22/2008-4/27/2009) +------------------- +Added magic.py to handle some very simple "magic" commands: + +Now the command: +"f 1" +will be re-interpreted as: +"f(1)" + +The command: +"f 1,2,3,4" +will be interpreted as: +f(1,2,3,4) + +Special commands "ls","cd", and "pwd" are interpreted separately + +Works with slices.py and shell.py + +Also fixed auto-indent magic. + +0.9.6.1 thru 0.9.6.3 (10/21/2008) +--------------------------------- +Added PySlices (slices.py and PySlices.py), a modified version of PyCrust. +PySlices is a "notebook interface" multi-line shell, ala Sage or Mathematica. +It uses Scintilla markers extensively, with red for input and blue for output. + +Modified crust.py to use a switch so it can load either a Shell or a Slices_Shell + +0.9.5 (12/23/2005) +------------------ + +Applied a series of enhancments by Franz Steinaeusler, Adi Sieker, and +Sebastian Haase, up until their 7-31-2005 version. (Their next +version broke some existing functionality, and added some confusing +hacks, and I didn't feel that the incremental gains were worth the +loss at that point so I stopped at 7-31-2005.) + +Their changes include the following: + +* The Autocomplete and Calltip windows can now be opened manually with + Ctrl-Space and Ctrl-Shift-Space. + +* In the stand alone PyCrust app the various option settings, window + size and position, and etc. are saved and restored at the next run. + +* Added a help dialog bound to the F1 key that shows the key bindings. + +* Added a new text completion function that suggests words from the + history. Bound to Shift-Return. + +* F11 will toggle the maximized state of the frame. + +* switched to Bind() from wx.EVT_*(). + +* Display of line numbers can be toggled. + +* F12 toggles a "free edit" mode of the shell buffer. This mode is + useful, for example, if you would like to remove some output or + errors or etc. from the buffer before doing a copy/paste. The free + edit mode is designated by the use of a red, non-flashing caret. + +* Ctrl-Shift-F will fold/unfold (hide/show) the selected lines. + + + +On top of these changes I (Robin Dunn) added the following: + +* General code cleanup and fixes. + +* Use wx.StandardPaths to determine the location of the config files. + +* Remove Orbtech attributions from the UI, they've been there long + enough. + +* Use wx.SP_LIVE_UPDATE on crust and filling windows. + +* Extended the saving of the config info and other new features to the + PyShell app too. Additionally, other apps that embed a PyCrust or a + PyShell can pass their own wx.Config object and have the Py code + save/restore its settings to/from there. + +* All of the classes with config info get an opportunity to save/load + their own settings instead of putting all the save/load code in one + place that then has to reach all over the place to do anything. + +* Enable editing of the startup python code, which will either be the + file pointed to by PYTHONSTARTUP or a file in the config dir if + PYTHONSTARTUP is not set in the environment. + +* Added an option to skip the running of the startup code when + PyShell or PyCrust starts. + +* PyCrust adds a pp(item) function to the shell's namespace that + pretty prints the item in the Display tab of the notebook. Added + code to raise that tab when pp() is called. + +* Added an option for whether to insert text for function parameters + when popping up the call tip. + +* Added Find and Find-Next functions that use the wx.FindReplaceDialog. + + + + + + +0.9.4 (1/25/2004 to //2004) +------------------------------ + +Removed wxd decorators in favor of new SWIG-generated docstrings. + +Removed docs tabs from crust interface: +* wxPython Docs +* wxSTC Docs + +Fixed Calltip tab refresh problem on Windows. + +shell.autoCompleteAutoHide added with default of False. + +Changed default namespace of Shell to __main__.__dict__, instead of an +empty dictionary. + + +0.9.3 (9/25/2003 to 1/24/2004) +------------------------------ + +Fun and games with dynamic renaming. Details of any other changes +were lost in the confusion. I'll try to do better in the future. + + +0.9.2 (5/3/2003 to 9/25/2003) +----------------------------- + +Changed to the new prefix-less "wx" package:: + + import wx + +instead of:: + + from wxPython import wx + +Fixed typo in ``PyWrap.py``:: + + if __name__ == '__main__': + main(sys.argv) + +should have been:: + + if __name__ == '__main__': + main() + +Added pretty-print Display tab to Crust, based on suggestion from +Jason Whitlark. + +Improved ``Can*`` checks in ``EditWindow``, since STC is too lenient, +particularly when it is set to read-only but returns True for +CanPaste() (seems like an STC bug to me):: + + def CanCopy(self): + """Return True if text is selected and can be copied.""" + return self.GetSelectionStart() != self.GetSelectionEnd() + + def CanCut(self): + """Return True if text is selected and can be cut.""" + return self.CanCopy() and self.CanEdit() + + def CanEdit(self): + """Return True if editing should succeed.""" + return not self.GetReadOnly() + + def CanPaste(self): + """Return True if pasting should succeed.""" + return stc.StyledTextCtrl.CanPaste(self) and self.CanEdit() + + +0.9.1 (3/21/2003 to 5/2/2003) +----------------------------- + +PyCrust is dead! Long live Py! + +* Renamed ``PyCrust`` package to ``py``. +* Moved code to wxPython's CVS repository. + +Fixed bug in ``introspect.py`` on introspecting objects occurring +immediately after a secondary prompt, like this:: + + >>> l = [1, 2, 3] + >>> for n in range(3): + ... l. <-- failed to popup autocomplete list + +Added documentation files: + +* PyManual.txt +* wxPythonManual.txt +* wxPythonPackage.txt +* wxPythonExamples.txt + +Added PyAlaMode and PyAlaCarte code editors. + +Major refactoring to support ``editor`` and ``shell`` from the same +base. + +Renamed program files: + +* ``PyCrustApp.py`` to ``PyCrust.py`` +* ``PyFillingApp.py`` to ``PyFilling.py`` +* ``PyShellApp.py`` to ``PyShell.py`` +* ``wrap.py`` to ``PyWrap.py`` + +Removed disabling of autocomplete for lists of 2000 items or more. +The current implementation of wxSTC can now handle lists this big. + +Improved handling of ``sys.path`` to mimic the standard Python shell. + + +0.9 (2/27/2003 to 3/20/2003) +---------------------------- + +Added fontIncrease, fontDecrease, fontDefault signals, receivers and +keybindings:: + + Ctrl+] Increase font size. + Ctrl+[ Decrease font size. + Ctrl+= Default font size. + +Continued enhancement of the decorator capability to provide better +documentation and docstrings for wxPython classes and functions. + +Introduced new tabbed interface: + +* Namespace +* Calltip +* Session +* Dispatcher +* wxPython Docs +* wxSTC Docs + +``Filling.tree`` now expands tuples as well as lists. (It should have +done this all along, I just never noticed this omission before.) + +Added this True/False test to all modules:: + + try: + True + except NameError: + True = 1==1 + False = 1==0 + +Added ``wxd`` directory with decoration classes. + + +0.8.2 (1/5/2003 to 2/26/2003) +----------------------------- + +Wrapped ``sys.ps1``, ``sys.ps2``, and ``sys.ps3`` in ``str()``. +(Thanks, Kieran Holland.) + +Fixed minor things found by PyChecker. + +Changed locals to use ``__main__.__dict__`` and added code to clean up +the namespace, making it as close to the regular Python environment as +possible. This solves the problem of pickling and unpickling +instances of classes defined in the shell. + +Made ``shell.PasteAndRun()`` a little more forgiving when it finds a +ps2 prompt line with no trailing space, such when you copy code from a +web page. + +Improved autocomplete behavior by adding these to shell:: + + self.AutoCompSetAutoHide(False) + self.AutoCompStops(' .,;:([)]}\'"\\<>%^&+-=*/|`') + +Added ``decor`` directory, ``decorator.py``, ``stcDecor.py``, and +``stcConstants.py``. These all serve the purpose of adding docstrings +to existing wxPython classes, in particular the ``wxStyledTextCtrl``. + +Added ``wrap.py``, a command line utility for running a wxPython app +with additional runtime-tools loaded, such as PyCrust (the only tool +at this point). + +Flushed the clipboard Cut/Copy operations so that selections will +exist in the clipboard even after PyCrust has been closed. + +Improved the suppression of docstrings for simple data types appearing +in the namespace viewer. + +Better handling of autocompletion with numeric types; no +autocompletion when typing a dot after an integer. If the +autocompletion is desired, type a space before the dot:: + + func = 3 . + +More Filling!!! The namespace tree is now dynamically updated. + + +0.8.1 (12/20/2002 to 12/25/2002) +-------------------------------- + +Improved keyboard handling with Autocomplete active. You can now use +Enter as well as Tab to select an item from the list. + +Disabled autocomplete for lists of 2000 items or more. The current +implementation of wxSTC can't handle lists this big. + +Changed ``filling`` to always display docstrings for objects. This is +useful for objects whose docstrings have been decorated, rather than +coming directly from the source code. (Hmmm. Sounds like someone is +doing some decorating. I wonder where that would be helpful? <wink>) + +Fixed handling of icon. Added ``images.py`` file. + + +0.8 (10/29/2002 to 12/16/2002) +------------------------------ + +Added "help" to startup banner info. + +Made all ``wx`` and ``stc`` imports explicit. No more ``import *``. + +Replaced use of the ``wx`` module's ``true`` and ``false`` with +Python's ``True`` and ``False``. + +Changed ``introspect.getRoot()`` to use ``tokenize`` module. This +does a slightly better job than the previous parsing routine and the +code is clearer. + +Improved handling of whitespace and empty types during introspection. + +Fixed cut/copy clipboard problem under Linux. (Robin Dunn rocks!!!) + +Added shell.about() which works like this:: + + >>> shell.about() + PyCrust Version: 0.8 + Shell Revision: 1.80 + Interpreter Revision: 1.15 + Python Version: 2.2.2 + wxPython Version: 2.3.3.1 + Platform: linux2 + +Added copy plus and paste plus to shell menu. + +Moved shell menu from ``shell.py`` to ``shellmenu.py``. + +Added ``sys.stdin.readlines()`` support. + +Added ``time.sleep()`` in ``readline()`` and ``OnIdle()`` event +handler to free up the CPU. + + +0.7.2 (2/22/2002 to 8/27/2002) +------------------------------ + +Tweaked ``getAttributeNames()`` to pick up a few more attributes:: + + '__bases__', '__class__', '__dict__', '__name__', 'func_closure', + 'func_code', 'func_defaults', 'func_dict', 'func_doc', + 'func_globals', 'func_name' + +Added a tests directory and unit tests. + +Improved support for empty types in the shell: ``[]``, ``()`` and +``{}`` as far as when call tips and autocompletion are available. + +Added support for the other triple string - ``''''''``. + +Refactored ``introspect.py`` to improve testability. + +Improved call tips for unbound methods by leaving the "self" +parameter, since unbound methods require an instance be passed. + +Fixed call tip bug where a tip was displayed when a "(" was typed +after an object that wasn't callable. + +Fixed ``getAllAttributeNames`` when ``str(object)`` fails. + +Added brace highlighting. (Thank you, Kevin Altis.) + +Fixed problem displaying unicode objects in ``PyFilling``. + +Changed how ``filling.py`` checks for expandable objects. Lists are +now expandable objects. + +Made the key handling more robust when there is an active text +selection that includes text prior to the last primary prompt. Thanks +to Raul Cota for pointing this out. + +Fixed wxSTC problem with brace highlighting and non-us keyboards. +(Thank you for the patch, Jean-Michel Fauth.) + +Added ``busy = wxBusyCursor()`` to key points in ``shell`` and +``filling``. + +Added ``OnCloseWindow`` handler to ``ShellFrame`` and ``CrustFrame``. + +Default to ``SetWrapMode(1)`` for shell and namespace viewer. + +Added ``shell.wrap()`` and ``shell.zoom()``. + +Added autoCompleteKeys hooks for Raul Cota. + +Cleaned up various little key handling bugs. + +Changed input methods to get values from shell, rather than dialog +boxes. Renamed ``readIn`` to ``readline`` and ``readRaw`` to +``raw_input``. + + +0.7.1 (12/12/2001 to 2/21/2002) +------------------------------- + +Fixed ``OnChar()`` issues effecting European keyboards, as reported by +Jean-Michel Fauth. + +Fixed ``introspect.py`` issue with xmlrpc objects reported by Kevin +Altis. + +Fixed some introspect/PyFilling issues with regard to Python 2.2. + +Fixed font background color as reported by Keith J. Farmer. (Thanks) + +Fixed problem with call tips and autocompletion inside multiline +commands as report by Kevin Altis. + +Improved ``OnKeyDown`` handling of cut/copy/paste operations based on +feedback from Syver Enstad. (Thanks) + +Added a ``shell.help()`` method to display some help info. + +Changed sort of items in the namespace viewer to case insensitive. + +Changed ``attributes.sort(lambda x, y: cmp(x.upper(), y.upper()))`` in +advance of an upcoming fix to an autocompletion matching bug in wxSTC. + +Improved support for ZODB by allowing namespace drilldown into BTrees. + +Added ``shell.PasteAndRun()`` to support pasting multiple commands into +the shell from the clipboard. Ctrl+Shift+V or v. + +Enter now always processes a command (or copies down a previous one.) +To insert a line break, press Ctrl+Enter. + +Escape key clears the current, unexecuted command. + +History retrieval changed to replace current command. Added new keys +to insert from history - Shift+Up and Shift+Down. + +Better call tips on objects with ``__call__`` methods. + +Improved call tip positioning calculation. + + +0.7 (10/15/2001 to 12/11/2001) +------------------------------ + +Changed how command history retrieval functions work. Added Alt-P, +Alt-N as keybindings for Retrieve-Previous, Retrieve-Next. + +Added full support for multi-line commands, similar to IDLE. + +Changed ``introspect.getAttributeNames()`` to do a case insensitive +sort. + +Changed Cut/Copy/Paste to deal with prompts intelligently. Cut and +Copy remove all prompts. Paste can handle prompted or not-prompted +text. + +Added ``CopyWithPrompts()`` method attached to Ctrl-Shift-C for those +times when you really do want all the prompts left intact. + +Improved handling of the shell's read-only zone. + +Changed ``CrustFrame.__init__`` parameter spec to include all +parameters allowed by a ``wxFrame``. + +Changed ``FillingText`` to be read-only. + +Renamed ``PyCrust.py`` to ``PyCrustApp.py`` to eliminate +package/module name conflicts that kept you from doing ``from PyCrust +import shell`` inside files located in the ``PyCrust`` directory. + +Renamed ``PyFilling.py`` to ``PyFillingApp.py`` and ``PyShell.py`` to +``PyShellApp.py`` to maintain consistency. + +Removed the ``__date__`` property from all modules. + +Fixed bug in ``introspect.getCallTip()``, reported by Kevin Altis. + + +0.6.1 (9/19/2001 to 10/12/2001) +------------------------------- + +Changed ``Shell.run()`` to always position to the end of existing +text, as suggested by Raul Cota. + +Changed ``introspect.getAllAttributeNames()`` to break circular +references in ``object.__class__``, which occurs in Zope/ZODB +extension classes. + +Changed ``filling.FillingTree.getChildren()`` to introspect extension +classes. + +Fixed minor bugs in ``introspect.getCallTip()`` that were interfering +with call tips for Zope/ZODB extension class methods. + +In preparation for wxPython 2.3.2, added code to fix a font sizing +problem. Versions of wxPython prior to 2.3.2 had a sizing bug on Win +platform where the font was 2 points larger than what was specified. + +Added a hack to ``introspect.getAllAttributeNames()`` to "wake up" +ZODB objects that are asleep - in a "ghost" state. Otherwise it +returns incomplete info. + + +0.6 (8/21/2001 to 9/12/2001) +---------------------------- + +Added ``PyFilling.py`` and ``filling.py``. + +``PyShell.py`` and ``PyFilling.py`` can now be run standalone, as well +as ``PyCrust.py``. + +Added ``crust.py`` and moved some code from ``PyCrust.py`` to it. + +Added command history retrieval features submitted by Richie Hindle. + +Changed ``shell.write()`` to replace line endings with OS-specific +endings. Changed ``shell.py`` and ``interpreter.py`` to use +``os.linesep`` in strings having hardcoded line endings. + +Added ``shell.redirectStdin()``, ``shell.redirectStdout()`` and +``shell.redirectStderr()`` to allow the surrounding app to toggle +requests that the specified ``sys.std*`` be redirected to the shell. +These can also be run from within the shell itself, of course. + +The shell now adds the current working directory "." to the search +path:: + + sys.path.insert(0, os.curdir) + +Added support for distutils installations. + + +0.5.4 (8/17/2001 to 8/20/2001) +------------------------------ + +Changed default font size under Linux to:: + + 'size' : 12, + 'lnsize' : 10, + +Changed ``Shell`` to expect a parameter referencing an Interpreter +class, rather than an intepreter instance, to facilitate subclassing +of Interpreter, which effectively broke when the Editor class was +eliminated. + +Fixed ``PyCrustAlaCarte.py``, which had been broken by previous +changes. + +Created ``InterpreterAlaCarte`` class as an example for use in the +demo. + +Split ``PyCrust.py`` into ``PyCrust.py`` and ``PyShell.py`` in +anticipation of ``PyFilling.py``. + + +0.5.3 (8/16/2001) +----------------- + +Added patch to ``PyCrust.py`` to fix wxPython bug:: + + wxID_SELECTALL = NewId() # This *should* be defined by wxPython. + + +0.5.2 (8/14/2001 to 8/15/2001) +------------------------------ + +Shortened module names by dropping "PyCrust" as a prefix. + +Changed ``version`` to ``VERSION`` in ``version`` module. + +Added Options menu to PyCrust application. + +Eliminated the Editor class (and editor module) by merging with Shell. +This means that Shell "is a" wxStyledTextCtrl rather than "has a". +There just wasn't enough non-gui code to justify the separation. +Plus, Shell will be much easier for gui toolkits/designers to deal +with now. + + +0.5.1 (8/10/2001 to 8/14/2001) +------------------------------ + +Added ``introspect`` module. + +Moved some functionality from ``PyCrustInterp`` to ``introspect``. + +Changed ``introspect.getRoot()`` to no longer remove whitespace from +the command. This was a remnant of a previous approach that, when +left as part of the current approach, turned out to be a really bad +thing. + +Changed ``introspect.getRoot()`` to allow commands of ``''``, ``""``, +``""""""``, ``[]``, ``()``, and ``{}`` to pass through. This allows +you to type them, followed by a dot, and get autocomplete options on +them. + +Changed ``introspect.getRoot()`` to identify some situations where +strings shouldn't be considered roots. For example:: + + >>> import PyCrust # To illustrate the potential problem. + >>> len('PyCrust.py') + +Typing the dot at the end of "PyCrust" in the second line above should +NOT result in an autocompletion list because "PyCrust" is part of a +string in this context, not a reference to the PyCrust module object. +Similar reasoning applies to call tips. For example:: + + >>> len('dir(') + +Typing the left paren at the end of "dir" should NOT result in a call +tip. + +Both features now behave properly in the examples given. However, +there is still the case where whitespace precedes the potential root +and that is NOT handled properly. For example:: + + >>> len('this is a dir(') + +and:: + + >>> len('This is PyCrust.py') + +More code needs to be written to handle more complex situations. + +Added ``locals=None`` parameter to ``Shell.__init__()``. + +Added support for magic attribute retrieval. Users can change this +with:: + + >>> shell.editor.autoCompleteIncludeMagic = 0 + +Added the ability to set filters on auto completion to exclude +attributes prefixed with a single or double underscore. Users can +exclude one or the other or both with:: + + >>> shell.editor.autoCompleteExcludeSingle = 1 + >>> shell.editor.autoCompleteExcludeDouble = 1 + + +0.5 (8/8/2001) +-------------- + +Mostly just a final version change before creating a release. + + +0.4 (8/4/2001 to 8/7/2001) +-------------------------- + +Changed version/revision handling. + +Fixed bugs. + + +0.3 (8/2/2001 to 8/3/2001) +-------------------------- + +Removed lots of cruft. + +Added lots of docstrings. + +Imported to CVS repository at SourceForge. + +Added call tips. + + +0.2 (7/30/2001 to 8/2/2001) +--------------------------- + +Renamed several files. + +Added command autocompletion. + +Added menus to PyCrust.py: File, Edit and Help. + +Added sample applications: ``PyCrustAlaCarte.py``, +``PyCrustAlaMode.py``, and ``PyCrustMinimus.py``. + + +0.1 (7/1/2001 to 7/19/2001) +--------------------------- + +Added basic syntax coloring much like Boa. + +Added read-only logging much like IDLE. + +Can retrieve a previous command by putting the cursor back on that +line and hitting enter. + +Stdin and raw_input operate properly so you can now do ``help()`` and +``license()`` without hanging. + +Redefined "quit", "exit", and "close" to display a better-than-nothing +response. + +Home key honors the prompt. + +Created SourceForge account, but nothing was posted. + + +In the beginning, there was pie... (7/1/2001) +--------------------------------------------- + +Blame it all on IDLE, Boa and PythonWin. I was using all three, got +frustrated with their dissimilarities, and began to let everyone know +how I felt. At the same time, Scintilla looked like an interesting +tool to build a shell around. And while I didn't receive much in the +way of positive feedback, let alone encouragement, I just couldn't let +go of the idea of a Scintilla-based Python shell. Then the PythonCard +project got to the point where they were talking about including a +shell in their development environment. That was all the incentive I +needed. PyCrust had to happen... diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/Py.ico b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/Py.ico Binary files differnew file mode 100644 index 0000000..eae6180 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/Py.ico diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyAlaCarte.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyAlaCarte.py new file mode 100644 index 0000000..53e0282 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyAlaCarte.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python2 +"""PyAlaCarte is a simple programmer's editor.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import wx +from wx import py + +import os +import sys + +class App(wx.App): + """PyAlaCarte standalone application.""" + + def __init__(self, filename=None): + self.filename = filename + wx.App.__init__(self, redirect=False) + + def OnInit(self): + wx.InitAllImageHandlers() + self.frame = py.editor.EditorFrame(filename=self.filename) + self.frame.Show() + self.SetTopWindow(self.frame) + return True + +def main(filename=None): + if not filename and len(sys.argv) > 1: + filename = sys.argv[1] + if filename: + filename = os.path.realpath(filename) + app = App(filename) + app.MainLoop() + +if __name__ == '__main__': + main() diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyAlaMode.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyAlaMode.py new file mode 100644 index 0000000..71c51a7 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyAlaMode.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python2 +"""PyAlaMode is a programmer's editor.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import wx +from wx import py + +import os +import sys + +class App(wx.App): + """PyAlaMode standalone application.""" + + def __init__(self, filename=None): + self.filename = filename + wx.App.__init__(self, redirect=False) + + def OnInit(self): + wx.InitAllImageHandlers() + self.frame = py.editor.EditorNotebookFrame(filename=self.filename) + self.frame.Show() + self.SetTopWindow(self.frame) + return True + +def main(filename=None): + if not filename and len(sys.argv) > 1: + filename = sys.argv[1] + if filename: + filename = os.path.realpath(filename) + app = App(filename) + app.MainLoop() + +if __name__ == '__main__': + main() diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyAlaModeTest.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyAlaModeTest.py new file mode 100644 index 0000000..78cffdf --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyAlaModeTest.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python2 +"""PyAlaModeTest is a programmer's editor.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import wx +from wx import py + +import os +import sys + +class App(wx.App): + """PyAlaModeTest standalone application.""" + + def __init__(self, filename=None): + self.filename = filename + wx.App.__init__(self, redirect=False) + + def OnInit(self): + wx.InitAllImageHandlers() + self.frame = py.editor.EditorShellNotebookFrame(filename=self.filename) + self.frame.Show() + self.SetTopWindow(self.frame) + return True + +def main(filename=None): + app = App(filename) + app.MainLoop() + +if __name__ == '__main__': + filename = None + if len(sys.argv) > 1: + filename = os.path.realpath(sys.argv[1]) + main(filename) diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyCrust.ico b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyCrust.ico Binary files differnew file mode 100644 index 0000000..eae6180 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyCrust.ico diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyCrust.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyCrust.py new file mode 100644 index 0000000..3ecbcc4 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyCrust.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python2 +"""PyCrust is a python shell and namespace browser application.""" + +# The next two lines, and the other code below that makes use of +# ``__main__`` and ``original``, serve the purpose of cleaning up the +# main namespace to look as much as possible like the regular Python +# shell environment. +import __main__ +original = __main__.__dict__.keys() + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import wx + +class App(wx.App): + """PyCrust standalone application.""" + + def OnInit(self): + import os + import wx + from wx import py + + self.SetAppName("pycrust") + confDir = wx.StandardPaths.Get().GetUserDataDir() + if not os.path.exists(confDir): + os.mkdir(confDir) + fileName = os.path.join(confDir, 'config') + self.config = wx.FileConfig(localFilename=fileName) + self.config.SetRecordDefaults(True) + + self.frame = py.crust.CrustFrame(config=self.config, dataDir=confDir) +## self.frame.startupFileName = os.path.join(confDir,'pycrust_startup') +## self.frame.historyFileName = os.path.join(confDir,'pycrust_history') + self.frame.Show() + self.SetTopWindow(self.frame) + return True + + +''' +The main() function needs to handle being imported, such as with the +pycrust script that wxPython installs: + + #!/usr/bin/env python2 + + from wx.py.PyCrust import main + main() +''' + +def main(): + """The main function for the PyCrust program.""" + # Cleanup the main namespace, leaving the App class. + import __main__ + md = __main__.__dict__ + keepers = original + keepers.append('App') + for key in md.keys(): + if key not in keepers: + del md[key] + # Create an application instance. + app = App(0) + # Mimic the contents of the standard Python shell's sys.path. + import sys + if sys.path[0]: + sys.path[0] = '' + # Add the application object to the sys module's namespace. + # This allows a shell user to do: + # >>> import sys + # >>> sys.app.whatever + sys.app = app + del sys + # Cleanup the main namespace some more. + if md.has_key('App') and md['App'] is App: + del md['App'] + if md.has_key('__main__') and md['__main__'] is __main__: + del md['__main__'] + # Start the wxPython event loop. + app.MainLoop() + +if __name__ == '__main__': + main() diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyFilling.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyFilling.py new file mode 100644 index 0000000..d5893c1 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyFilling.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python2 +"""PyFilling is a python namespace inspection application.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +# We use this object to get more introspection when run standalone. +app = None + +import filling + +# These are imported just to have something interesting to inspect. +import crust +import interpreter +import introspect +import pseudo +import shell +import sys +import wx + +class App(filling.App): + def OnInit(self): + filling.App.OnInit(self) + self.root = self.fillingFrame.filling.tree.root + return True + +def main(): + """Create and run the application.""" + global app + app = App(0) + app.fillingFrame.filling.tree.Expand(app.root) + app.MainLoop() + +if __name__ == '__main__': + main() diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyShell.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyShell.py new file mode 100644 index 0000000..d48409c --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyShell.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python2 +"""PyShell is a python shell application.""" + +# The next two lines, and the other code below that makes use of +# ``__main__`` and ``original``, serve the purpose of cleaning up the +# main namespace to look as much as possible like the regular Python +# shell environment. +import __main__ +original = __main__.__dict__.keys() + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import wx +import os + +class App(wx.App): + """PyShell standalone application.""" + + def OnInit(self): + import os + import wx + from wx import py + + self.SetAppName("pyshell") + confDir = wx.StandardPaths.Get().GetUserDataDir() + if not os.path.exists(confDir): + os.mkdir(confDir) + fileName = os.path.join(confDir, 'config') + self.config = wx.FileConfig(localFilename=fileName) + self.config.SetRecordDefaults(True) + + self.frame = py.shell.ShellFrame(config=self.config, dataDir=confDir) + self.frame.Show() + self.SetTopWindow(self.frame) + return True + +''' +The main() function needs to handle being imported, such as with the +pyshell script that wxPython installs: + + #!/usr/bin/env python2 + + from wx.py.PyShell import main + main() +''' + +def main(): + """The main function for the PyShell program.""" + # Cleanup the main namespace, leaving the App class. + import __main__ + md = __main__.__dict__ + keepers = original + keepers.append('App') + for key in md.keys(): + if key not in keepers: + del md[key] + # Create an application instance. + app = App(0) + # Cleanup the main namespace some more. + if md.has_key('App') and md['App'] is App: + del md['App'] + if md.has_key('__main__') and md['__main__'] is __main__: + del md['__main__'] + # Mimic the contents of the standard Python shell's sys.path. + import sys + if sys.path[0]: + sys.path[0] = '' + # Add the application object to the sys module's namespace. + # This allows a shell user to do: + # >>> import sys + # >>> sys.app.whatever + sys.app = app + del sys + # Start the wxPython event loop. + app.MainLoop() + +if __name__ == '__main__': + main() diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PySlices.ico b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PySlices.ico Binary files differnew file mode 100644 index 0000000..b237a84 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PySlices.ico diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PySlices.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PySlices.py new file mode 100644 index 0000000..d47c900 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PySlices.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python2 +"""PySlices is a python block code editor / shell and namespace browser application.""" + +# The next two lines, and the other code below that makes use of +# ``__main__`` and ``original``, serve the purpose of cleaning up the +# main namespace to look as much as possible like the regular Python +# shell environment. +import __main__ +original = __main__.__dict__.keys() + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com> / " +__author__ += "David N. Mashburn <david.n.mashburn@gmail.com>" +__cvsid__ = "$Id: PySlices.py 36607 2005-12-30 23:02:03Z RD $" # Hmmm... +__revision__ = "$Revision: 36607 $"[11:-2] #Hmmm... + +import wx +import os + +class App(wx.App): + """PySlices standalone application.""" + + def __init__(self, filename=None): + self.filename = filename + import wx + wx.App.__init__(self, redirect=False) + + + def OnInit(self): + import os + import wx + from wx import py + + self.SetAppName("pyslices") + confDir = wx.StandardPaths.Get().GetUserDataDir() + if not os.path.exists(confDir): + os.mkdir(confDir) + fileName = os.path.join(confDir, 'config') + self.config = wx.FileConfig(localFilename=fileName) + self.config.SetRecordDefaults(True) + + self.frame = py.crustslices.CrustSlicesFrame(config=self.config, dataDir=confDir, + filename=self.filename) +## self.frame.startupFileName = os.path.join(confDir,'pycrust_startup') +## self.frame.historyFileName = os.path.join(confDir,'pycrust_history') + self.frame.Show() + self.SetTopWindow(self.frame) + return True + + +''' +The main() function needs to handle being imported, such as with the +pycrust script that wxPython installs: + + #!/usr/bin/env python2 + + from wx.py.PySlices import main + main() +''' + +def main(filename=None): + """The main function for the PySlices program.""" + # Cleanup the main namespace, leaving the App class. + import sys + if not filename and len(sys.argv) > 1: + filename = sys.argv[1] + if filename: + filename = os.path.realpath(filename) + + import __main__ + md = __main__.__dict__ + keepers = original + keepers.append('App') + keepers.append('filename') + for key in md.keys(): + if key not in keepers: + del md[key] + # Create an application instance. + app = App(filename=filename) + # Mimic the contents of the standard Python shell's sys.path. + import sys + if sys.path[0]: + sys.path[0] = '' + # Add the application object to the sys module's namespace. + # This allows a shell user to do: + # >>> import sys + # >>> sys.app.whatever + sys.app = app + del sys + # Cleanup the main namespace some more. + if md.has_key('App') and md['App'] is App: + del md['App'] + if md.has_key('filename') and md['filename'] is filename: + del md['filename'] + if md.has_key('__main__') and md['__main__'] is __main__: + del md['__main__'] + # Start the wxPython event loop. + app.MainLoop() + +if __name__ == '__main__': + main() diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PySlicesShell.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PySlicesShell.py new file mode 100644 index 0000000..28b500e --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PySlicesShell.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python2 +"""PySlicesShell is a python shell application.""" + +# The next two lines, and the other code below that makes use of +# ``__main__`` and ``original``, serve the purpose of cleaning up the +# main namespace to look as much as possible like the regular Python +# shell environment. +import __main__ +original = __main__.__dict__.keys() + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id: PySlicesShell.py 41078 2006-09-09 00:38:53Z RD $" +__revision__ = "$Revision: 41078 $"[11:-2] + +import wx +import os + +class App(wx.App): + """PySlicesShell standalone application.""" + + def __init__(self, filename=None): + self.filename = filename + import wx + wx.App.__init__(self, redirect=False) + + def OnInit(self): + import os + import wx + from wx import py + + self.SetAppName("pysliceshell") + confDir = wx.StandardPaths.Get().GetUserDataDir() + if not os.path.exists(confDir): + os.mkdir(confDir) + fileName = os.path.join(confDir, 'config') + self.config = wx.FileConfig(localFilename=fileName) + self.config.SetRecordDefaults(True) + + self.frame = py.sliceshell.SlicesShellFrame(config=self.config, + dataDir=confDir, + filename=self.filename) + self.frame.Show() + self.SetTopWindow(self.frame) + return True + +''' +The main() function needs to handle being imported, such as with the +pyshell script that wxPython installs: + + #!/usr/bin/env python2 + + from wx.py.PySlicesShell import main + main() +''' + +def main(filename=None): + """The main function for the PySlicesShell program.""" + # Cleanup the main namespace, leaving the App class. + import sys + if not filename and len(sys.argv) > 1: + filename = sys.argv[1] + if filename: + filename = os.path.realpath(filename) + + import __main__ + md = __main__.__dict__ + keepers = original + keepers.append('App') + keepers.append('filename') + for key in md.keys(): + if key not in keepers: + del md[key] + # Create an application instance. + app = App(filename=filename) + # Cleanup the main namespace some more. + if md.has_key('App') and md['App'] is App: + del md['App'] + if md.has_key('filename') and md['filename'] is filename: + del md['filename'] + if md.has_key('__main__') and md['__main__'] is __main__: + del md['__main__'] + # Mimic the contents of the standard Python shell's sys.path. + import sys + if sys.path[0]: + sys.path[0] = '' + # Add the application object to the sys module's namespace. + # This allows a shell user to do: + # >>> import sys + # >>> sys.app.whatever + sys.app = app + del sys + # Start the wxPython event loop. + app.MainLoop() + +if __name__ == '__main__': + main() diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyWrap.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyWrap.py new file mode 100644 index 0000000..fafa382 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/PyWrap.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python2 +"""PyWrap is a command line utility that runs a wxPython program with +additional runtime-tools, such as PyCrust.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import wx +from wx import py + +import os +import sys + +def wrap(app): + wx.InitAllImageHandlers() + frame = py.crust.CrustFrame() + frame.SetSize((750, 525)) + frame.Show(True) + frame.shell.interp.locals['app'] = app + app.MainLoop() + +def main(modulename=None): + sys.path.insert(0, os.curdir) + if not modulename: + if len(sys.argv) < 2: + print "Please specify a module name." + raise SystemExit + modulename = sys.argv[1] + if modulename.endswith('.py'): + modulename = modulename[:-3] + module = __import__(modulename) + # Find the App class. + App = None + d = module.__dict__ + for item in d.keys(): + try: + if issubclass(d[item], wx.App): + App = d[item] + except (NameError, TypeError): + pass + if App is None: + print "No App class was found." + raise SystemExit + app = App() + wrap(app) + +if __name__ == '__main__': + main() diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/README.txt b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/README.txt new file mode 100644 index 0000000..2d70db7 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/README.txt @@ -0,0 +1,83 @@ +===================================== + PyCrust - The Flakiest Python Shell +===================================== + +Half-baked by Patrick K. O'Brien (pobrien@orbtech.com) + +Orbtech - "Your source for Python programming expertise." +Sample all our half-baked Python goods at www.orbtech.com. + + +What is PyCrust? +---------------- + +PyCrust is an interactive Python environment written in Python. +PyCrust components can run standalone or be integrated into other +development environments and/or other Python applications. + +PyCrust comes with an interactive Python shell (PyShell), an +interactive namespace/object tree control (PyFilling) and an +integrated, split-window combination of the two (PyCrust). + + +What is PyCrust good for? +------------------------- + +Have you ever tried to bake a pie without one? Well, you shouldn't +build a Python program without a PyCrust either. + + +What else do I need to use PyCrust? +----------------------------------- + +PyCrust requires Python 2.2 or later, and wxPython 2.4 or later. +PyCrust uses wxPython and the Scintilla wrapper (wxStyledTextCtrl). +Python is available at http://www.python.org/. wxPython is available +at http://www.wxpython.org/. + + +Where can I get the latest version of PyCrust? +---------------------------------------------- + +The latest production version ships with wxPython. The latest +developer version is available in the wxWindows CVS at: +http://cvs.wxwindows.org/viewcvs.cgi/ + + +Where is the PyCrust project hosted? +------------------------------------ + +The old answer was "At SourceForge, of course." The SourceForge +summary page is still available at: +http://sourceforge.net/projects/pycrust/ + +The new answer is that there is no longer a need for a separate +project. Simply install wxPython and you'll have everything you need. + + +I found a bug in PyCrust, what do I do with it? +----------------------------------------------- + +You can send it to me at pobrien@orbtech.com. + + +I want a new feature added to PyCrust. Will you do it? +------------------------------------------------------ + +Flattery and money will get you anything. Short of that, you can send +me a request and I'll see what I can do. + + +Does PyCrust have a mailing list full of wonderful people? +---------------------------------------------------------- + +As a matter of fact, we do. Join the PyCrust mailing lists at: +http://sourceforge.net/mail/?group_id=31263 + + +What is the CVS information for this README file? +------------------------------------------------- + +$Date$ +$Revision$ +$Id$ diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/__init__.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/__init__.py new file mode 100644 index 0000000..edf6fb5 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/__init__.py @@ -0,0 +1,22 @@ +"""The py package, formerly the PyCrust package.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import buffer +import crust +import crustslices +import dispatcher +import document +import editor +import editwindow +import filling +import frame +import images +import interpreter +import introspect +import pseudo +import shell +import sliceshell +import version diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/buffer.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/buffer.py new file mode 100644 index 0000000..7766694 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/buffer.py @@ -0,0 +1,138 @@ +"""Buffer class.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +from interpreter import Interpreter +import imp +import os +import sys + +import document + + +class Buffer: + """Buffer class.""" + + id = 0 + + def __init__(self, filename=None): + """Create a Buffer instance.""" + Buffer.id += 1 + self.id = Buffer.id + self.interp = Interpreter(locals={}) + self.name = '' + self.editors = {} + self.editor = None + self.modules = sys.modules.keys() + self.syspath = sys.path[:] + while True: + try: + self.syspath.remove('') + except ValueError: + break + while True: + try: + self.syspath.remove('.') + except ValueError: + break + self.open(filename) + + def addEditor(self, editor): + """Add an editor.""" + self.editor = editor + self.editors[editor.id] = editor + + def hasChanged(self): + """Return True if text in editor has changed since last save.""" + if self.editor: + return self.editor.hasChanged() + else: + return False + + def new(self, filepath): + """New empty buffer.""" + if not filepath: + return + if os.path.exists(filepath): + self.confirmed = self.overwriteConfirm(filepath) + else: + self.confirmed = True + + def open(self, filename): + """Open file into buffer.""" + self.doc = document.Document(filename) + self.name = self.doc.filename or ('Untitled:' + str(self.id)) + self.modulename = self.doc.filebase + # XXX This should really make sure filedir is first item in syspath. + # XXX Or maybe this should be moved to the update namespace method. + if self.doc.filedir and self.doc.filedir not in self.syspath: + # To create the proper context for updateNamespace. + self.syspath.insert(0, self.doc.filedir) + if self.doc.filepath and os.path.exists(self.doc.filepath): + self.confirmed = True + if self.editor: + text = self.doc.read() + self.editor._setBuffer(buffer=self, text=text) + + def overwriteConfirm(self, filepath): + """Confirm overwriting an existing file.""" + return False + + def save(self): + """Save buffer.""" + filepath = self.doc.filepath + if not filepath: + return # XXX Get filename + if not os.path.exists(filepath): + self.confirmed = True + if not self.confirmed: + self.confirmed = self.overwriteConfirm(filepath) + if self.confirmed: + self.doc.write(self.editor.getText()) + if self.editor: + self.editor.setSavePoint() + + def saveAs(self, filename): + """Save buffer.""" + self.doc = document.Document(filename) + self.name = self.doc.filename + self.modulename = self.doc.filebase + self.save() + + def updateNamespace(self): + """Update the namespace for autocompletion and calltips. + + Return True if updated, False if there was an error.""" + if not self.interp or not hasattr(self.editor, 'getText'): + return False + syspath = sys.path + sys.path = self.syspath + text = self.editor.getText() + text = text.replace('\r\n', '\n') + text = text.replace('\r', '\n') + name = self.modulename or self.name + module = imp.new_module(name) + newspace = module.__dict__.copy() + try: + try: + code = compile(text, name, 'exec') + except: + raise +# return False + try: + exec code in newspace + except: + raise +# return False + else: + # No problems, so update the namespace. + self.interp.locals.clear() + self.interp.locals.update(newspace) + return True + finally: + sys.path = syspath + for m in sys.modules.keys(): + if m not in self.modules: + del sys.modules[m] diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/crust.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/crust.py new file mode 100644 index 0000000..aed30a1 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/crust.py @@ -0,0 +1,379 @@ +"""Crust combines the shell and filling into one control.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import wx + +import os +import pprint +import re +import sys + +import dispatcher +import editwindow +from filling import Filling +import frame +from shell import Shell +from version import VERSION + + +class Crust(wx.SplitterWindow): + """Crust based on SplitterWindow.""" + + name = 'Crust' + revision = __revision__ + sashoffset = 300 + + def __init__(self, parent, id=-1, pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.SP_3D|wx.SP_LIVE_UPDATE, + name='Crust Window', rootObject=None, rootLabel=None, + rootIsNamespace=True, intro='', locals=None, + InterpClass=None, + startupScript=None, execStartupScript=True, + *args, **kwds): + """Create Crust instance.""" + wx.SplitterWindow.__init__(self, parent, id, pos, size, style, name) + + # Turn off the tab-traversal style that is automatically + # turned on by wx.SplitterWindow. We do this because on + # Windows the event for Ctrl-Enter is stolen and used as a + # navigation key, but the Shell window uses it to insert lines. + style = self.GetWindowStyle() + self.SetWindowStyle(style & ~wx.TAB_TRAVERSAL) + + self.shell = Shell(parent=self, introText=intro, + locals=locals, InterpClass=InterpClass, + startupScript=startupScript, + execStartupScript=execStartupScript, + *args, **kwds) + + self.editor = self.shell + if rootObject is None: + rootObject = self.shell.interp.locals + self.notebook = wx.Notebook(parent=self, id=-1) + self.shell.interp.locals['notebook'] = self.notebook + self.filling = Filling(parent=self.notebook, + rootObject=rootObject, + rootLabel=rootLabel, + rootIsNamespace=rootIsNamespace) + # Add 'filling' to the interpreter's locals. + self.shell.interp.locals['filling'] = self.filling + self.notebook.AddPage(page=self.filling, text='Namespace', select=True) + + self.display = Display(parent=self.notebook) + self.notebook.AddPage(page=self.display, text='Display') + # Add 'pp' (pretty print) to the interpreter's locals. + self.shell.interp.locals['pp'] = self.display.setItem + self.display.nbTab = self.notebook.GetPageCount()-1 + + self.calltip = Calltip(parent=self.notebook) + self.notebook.AddPage(page=self.calltip, text='Calltip') + + self.sessionlisting = SessionListing(parent=self.notebook) + self.notebook.AddPage(page=self.sessionlisting, text='History') + + self.dispatcherlisting = DispatcherListing(parent=self.notebook) + self.notebook.AddPage(page=self.dispatcherlisting, text='Dispatcher') + + + # Initialize in an unsplit mode, and check later after loading + # settings if we should split or not. + self.shell.Hide() + self.notebook.Hide() + self.Initialize(self.shell) + self._shouldsplit = True + wx.CallAfter(self._CheckShouldSplit) + self.SetMinimumPaneSize(100) + + self.Bind(wx.EVT_SIZE, self.SplitterOnSize) + self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnChanged) + self.Bind(wx.EVT_SPLITTER_DCLICK, self.OnSashDClick) + + def _CheckShouldSplit(self): + if self._shouldsplit: + self.SplitHorizontally(self.shell, self.notebook, -self.sashoffset) + self.lastsashpos = self.GetSashPosition() + else: + self.lastsashpos = -1 + self.issplit = self.IsSplit() + + def ToggleTools(self): + """Toggle the display of the filling and other tools""" + if self.issplit: + self.Unsplit() + else: + self.SplitHorizontally(self.shell, self.notebook, -self.sashoffset) + self.lastsashpos = self.GetSashPosition() + self.issplit = self.IsSplit() + + def ToolsShown(self): + return self.issplit + + def OnChanged(self, event): + """update sash offset from the bottom of the window""" + self.sashoffset = self.GetSize().height - event.GetSashPosition() + self.lastsashpos = event.GetSashPosition() + event.Skip() + + def OnSashDClick(self, event): + self.Unsplit() + self.issplit = False + + # Make the splitter expand the top window when resized + def SplitterOnSize(self, event): + splitter = event.GetEventObject() + sz = splitter.GetSize() + splitter.SetSashPosition(sz.height - self.sashoffset, True) + event.Skip() + + + def LoadSettings(self, config): + self.shell.LoadSettings(config) + self.filling.LoadSettings(config) + + pos = config.ReadInt('Sash/CrustPos', 400) + wx.CallAfter(self.SetSashPosition, pos) + def _updateSashPosValue(): + sz = self.GetSize() + self.sashoffset = sz.height - self.GetSashPosition() + wx.CallAfter(_updateSashPosValue) + zoom = config.ReadInt('View/Zoom/Display', -99) + if zoom != -99: + self.display.SetZoom(zoom) + self.issplit = config.ReadInt('Sash/IsSplit', True) + if not self.issplit: + self._shouldsplit = False + + def SaveSettings(self, config): + self.shell.SaveSettings(config) + self.filling.SaveSettings(config) + + if self.lastsashpos != -1: + config.WriteInt('Sash/CrustPos', self.lastsashpos) + config.WriteInt('Sash/IsSplit', self.issplit) + config.WriteInt('View/Zoom/Display', self.display.GetZoom()) + +class Display(editwindow.EditWindow): + """STC used to display an object using Pretty Print.""" + + def __init__(self, parent, id=-1, pos=wx.DefaultPosition, + size=wx.DefaultSize, + style=wx.CLIP_CHILDREN | wx.SUNKEN_BORDER, + static=False): + """Create Display instance.""" + editwindow.EditWindow.__init__(self, parent, id, pos, size, style) + # Configure various defaults and user preferences. + self.SetReadOnly(True) + self.SetWrapMode(False) + if not static: + dispatcher.connect(receiver=self.push, signal='Interpreter.push') + + def push(self, command, more): + """Receiver for Interpreter.push signal.""" + self.Refresh() + + def Refresh(self): + if not hasattr(self, "item"): + return + self.SetReadOnly(False) + text = pprint.pformat(self.item) + self.SetText(text) + self.SetReadOnly(True) + + def setItem(self, item): + """Set item to pretty print in the notebook Display tab.""" + self.item = item + self.Refresh() + if self.GetParent().GetSelection() != self.nbTab: + focus = wx.Window.FindFocus() + self.GetParent().SetSelection(self.nbTab) + wx.CallAfter(focus.SetFocus) + + +# TODO: Switch this to a editwindow.EditWindow +class Calltip(wx.TextCtrl): + """Text control containing the most recent shell calltip.""" + + def __init__(self, parent=None, id=-1,ShellClassName='Shell'): + style = (wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2) + wx.TextCtrl.__init__(self, parent, id, style=style) + self.SetBackgroundColour(wx.Colour(255, 255, 208)) + self.ShellClassName=ShellClassName + dispatcher.connect(receiver=self.display, signal=self.ShellClassName+'.calltip') + + df = self.GetFont() + font = wx.Font(df.GetPointSize(), wx.TELETYPE, wx.NORMAL, wx.NORMAL) + self.SetFont(font) + + def display(self, calltip): + """Receiver for """+self.ShellClassName+""".calltip signal.""" + ## self.SetValue(calltip) # Caused refresh problem on Windows. + self.Clear() + self.AppendText(calltip) + self.SetInsertionPoint(0) + + +# TODO: Switch this to a editwindow.EditWindow +class SessionListing(wx.TextCtrl): + """Text control containing all commands for session.""" + + def __init__(self, parent=None, id=-1,ShellClassName='Shell'): + style = (wx.TE_MULTILINE | wx.TE_READONLY | + wx.TE_RICH2 | wx.TE_DONTWRAP) + wx.TextCtrl.__init__(self, parent, id, style=style) + dispatcher.connect(receiver=self.addHistory, + signal=ShellClassName+".addHistory") + dispatcher.connect(receiver=self.clearHistory, + signal=ShellClassName+".clearHistory") + dispatcher.connect(receiver=self.loadHistory, + signal=ShellClassName+".loadHistory") + + df = self.GetFont() + font = wx.Font(df.GetPointSize(), wx.TELETYPE, wx.NORMAL, wx.NORMAL) + self.SetFont(font) + + def loadHistory(self, history): + # preload the existing history, if any + hist = history[:] + hist.reverse() + self.SetValue('\n'.join(hist) + '\n') + self.SetInsertionPointEnd() + + def addHistory(self, command): + if command: + self.SetInsertionPointEnd() + self.AppendText(command + '\n') + + def clearHistory(self): + self.SetValue("") + + +class DispatcherListing(wx.TextCtrl): + """Text control containing all dispatches for session.""" + + def __init__(self, parent=None, id=-1): + style = (wx.TE_MULTILINE | wx.TE_READONLY | + wx.TE_RICH2 | wx.TE_DONTWRAP) + wx.TextCtrl.__init__(self, parent, id, style=style) + dispatcher.connect(receiver=self.spy) + + df = self.GetFont() + font = wx.Font(df.GetPointSize(), wx.TELETYPE, wx.NORMAL, wx.NORMAL) + self.SetFont(font) + + def spy(self, signal, sender): + """Receiver for Any signal from Any sender.""" + text = '%r from %s' % (signal, sender) + self.SetInsertionPointEnd() + start, end = self.GetSelection() + if start != end: + self.SetSelection(0, 0) + self.AppendText(text + '\n') + + + +class CrustFrame(frame.Frame, frame.ShellFrameMixin): + """Frame containing all the PyCrust components.""" + + name = 'CrustFrame' + revision = __revision__ + + + def __init__(self, parent=None, id=-1, title='PyCrust', + pos=wx.DefaultPosition, size=wx.DefaultSize, + style=wx.DEFAULT_FRAME_STYLE, + rootObject=None, rootLabel=None, rootIsNamespace=True, + locals=None, InterpClass=None, + config=None, dataDir=None, + *args, **kwds): + """Create CrustFrame instance.""" + frame.Frame.__init__(self, parent, id, title, pos, size, style, + shellName='PyCrust') + frame.ShellFrameMixin.__init__(self, config, dataDir) + + if size == wx.DefaultSize: + self.SetSize((800, 600)) + + intro = 'PyCrust %s - The Flakiest Python Shell' % VERSION + + self.SetStatusText(intro.replace('\n', ', ')) + self.crust = Crust(parent=self, intro=intro, + rootObject=rootObject, + rootLabel=rootLabel, + rootIsNamespace=rootIsNamespace, + locals=locals, + InterpClass=InterpClass, + startupScript=self.startupScript, + execStartupScript=self.execStartupScript, + *args, **kwds) + self.shell = self.crust.shell + + # Override the filling so that status messages go to the status bar. + self.crust.filling.tree.setStatusText = self.SetStatusText + + # Override the shell so that status messages go to the status bar. + self.shell.setStatusText = self.SetStatusText + + self.shell.SetFocus() + self.LoadSettings() + + + def OnClose(self, event): + """Event handler for closing.""" + self.SaveSettings() + self.crust.shell.destroy() + self.Destroy() + + + def OnAbout(self, event): + """Display an About window.""" + title = 'About PyCrust' + text = 'PyCrust %s\n\n' % VERSION + \ + 'Yet another Python shell, only flakier.\n\n' + \ + 'Half-baked by Patrick K. O\'Brien,\n' + \ + 'the other half is still in the oven.\n\n' + \ + 'Shell Revision: %s\n' % self.shell.revision + \ + 'Interpreter Revision: %s\n\n' % self.shell.interp.revision + \ + 'Platform: %s\n' % sys.platform + \ + 'Python Version: %s\n' % sys.version.split()[0] + \ + 'wxPython Version: %s\n' % wx.VERSION_STRING + \ + ('\t(%s)\n' % ", ".join(wx.PlatformInfo[1:])) + dialog = wx.MessageDialog(self, text, title, + wx.OK | wx.ICON_INFORMATION) + dialog.ShowModal() + dialog.Destroy() + + + def ToggleTools(self): + """Toggle the display of the filling and other tools""" + return self.crust.ToggleTools() + + def ToolsShown(self): + return self.crust.ToolsShown() + + def OnHelp(self, event): + """Show a help dialog.""" + frame.ShellFrameMixin.OnHelp(self, event) + + def LoadSettings(self): + if self.config is not None: + frame.ShellFrameMixin.LoadSettings(self) + frame.Frame.LoadSettings(self, self.config) + self.crust.LoadSettings(self.config) + + + def SaveSettings(self, force=False): + if self.config is not None: + frame.ShellFrameMixin.SaveSettings(self,force) + if self.autoSaveSettings or force: + frame.Frame.SaveSettings(self, self.config) + self.crust.SaveSettings(self.config) + + + def DoSaveSettings(self): + if self.config is not None: + self.SaveSettings(force=True) + self.config.Flush() + diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/crustslices.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/crustslices.py new file mode 100644 index 0000000..aa7bd23 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/crustslices.py @@ -0,0 +1,416 @@ +"""PySlices combines the slices and filling into one control.""" + +__author__ = "David N. Mashburn <david.n.mashburn@gmail.com> / " +__author__ += "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id: crustslices.py 44235 2007-01-17 23:05:14Z RD $" +__revision__ = "$Revision: 44235 $"[11:-2] + +import wx + +import os +import pprint +import re +import sys + +import dispatcher +import crust +import document +import editwindow +import editor +from filling import Filling +import frame +from sliceshell import SlicesShell +from version import VERSION + + +class CrustSlices(crust.Crust): + """Slices based on SplitterWindow.""" + + name = 'Slices' + revision = __revision__ + sashoffset = 300 + + def __init__(self, parent, id=-1, pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.SP_3D|wx.SP_LIVE_UPDATE, + name='Slices Window', rootObject=None, rootLabel=None, + rootIsNamespace=True, intro='', locals=None, + InterpClass=None, + startupScript=None, execStartupScript=True, + showPySlicesTutorial=True, + enableShellMode=False, hideFoldingMargin=False, + *args, **kwds): + """Create CrustSlices instance.""" + wx.SplitterWindow.__init__(self, parent, id, pos, size, style, name) + + # Turn off the tab-traversal style that is automatically + # turned on by wx.SplitterWindow. We do this because on + # Windows the event for Ctrl-Enter is stolen and used as a + # navigation key, but the SlicesShell window uses it to insert lines. + style = self.GetWindowStyle() + self.SetWindowStyle(style & ~wx.TAB_TRAVERSAL) + + self.sliceshell = SlicesShell(parent=self, introText=intro, + locals=locals, InterpClass=InterpClass, + startupScript=startupScript, + execStartupScript=execStartupScript, + showPySlicesTutorial=showPySlicesTutorial, + enableShellMode=enableShellMode, + hideFoldingMargin=hideFoldingMargin, + *args, **kwds) + + self.editor = self.sliceshell + self.shell = self.sliceshell + if rootObject is None: + rootObject = self.sliceshell.interp.locals + self.notebook = wx.Notebook(parent=self, id=-1) + self.sliceshell.interp.locals['notebook'] = self.notebook + self.filling = Filling(parent=self.notebook, + rootObject=rootObject, + rootLabel=rootLabel, + rootIsNamespace=rootIsNamespace) + # Add 'filling' to the interpreter's locals. + self.sliceshell.interp.locals['filling'] = self.filling + self.notebook.AddPage(page=self.filling, text='Namespace', select=True) + + self.display = crust.Display(parent=self.notebook) + self.notebook.AddPage(page=self.display, text='Display') + # Add 'pp' (pretty print) to the interpreter's locals. + self.sliceshell.interp.locals['pp'] = self.display.setItem + self.display.nbTab = self.notebook.GetPageCount()-1 + + self.calltip = crust.Calltip(parent=self.notebook,ShellClassName='SlicesShell') + self.notebook.AddPage(page=self.calltip, text='Calltip') + + self.sessionlisting = crust.SessionListing(parent=self.notebook,ShellClassName='SlicesShell') + self.notebook.AddPage(page=self.sessionlisting, text='History') + + self.dispatcherlisting = crust.DispatcherListing(parent=self.notebook) + self.notebook.AddPage(page=self.dispatcherlisting, text='Dispatcher') + + + # Initialize in an unsplit mode, and check later after loading + # settings if we should split or not. + self.sliceshell.Hide() + self.notebook.Hide() + self.Initialize(self.sliceshell) + self._shouldsplit = True + wx.CallAfter(self._CheckShouldSplit) + self.SetMinimumPaneSize(100) + + self.Bind(wx.EVT_SIZE, self.SplitterOnSize) + self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnChanged) + self.Bind(wx.EVT_SPLITTER_DCLICK, self.OnSashDClick) + +class CrustSlicesFrame(crust.CrustFrame): + """Frame containing all the PySlices components.""" + + name = 'SliceFrame' + revision = __revision__ + + + def __init__(self, parent=None, id=-1, title='PySlices', + pos=wx.DefaultPosition, size=wx.DefaultSize, + style=wx.DEFAULT_FRAME_STYLE, + rootObject=None, rootLabel=None, rootIsNamespace=True, + locals=None, InterpClass=None, + config=None, dataDir=None, filename=None, + *args, **kwds): + """Create CrustFrame instance.""" + frame.Frame.__init__(self, parent, id, title, pos, size, style, + shellName='PySlices') + frame.ShellFrameMixin.__init__(self, config, dataDir) + + if size == wx.DefaultSize: + self.SetSize((800, 600)) + + intro = 'PySlices %s - The Flakiest Python Shell... Cut up!' % VERSION + + self.SetStatusText(intro.replace('\n', ', ')) + self.crust = CrustSlices(parent=self, intro=intro, + rootObject=rootObject, + rootLabel=rootLabel, + rootIsNamespace=rootIsNamespace, + locals=locals, + InterpClass=InterpClass, + startupScript=self.startupScript, + execStartupScript=self.execStartupScript, + showPySlicesTutorial=self.showPySlicesTutorial, + enableShellMode=self.enableShellMode, + hideFoldingMargin=self.hideFoldingMargin, + *args, **kwds) + self.sliceshell = self.crust.sliceshell + self.buffer = self.sliceshell.buffer + # Override the filling so that status messages go to the status bar. + self.crust.filling.tree.setStatusText = self.SetStatusText + + # Override the shell so that status messages go to the status bar. + self.sliceshell.setStatusText = self.SetStatusText + + self.sliceshell.SetFocus() + self.LoadSettings() + + self.currentDirectory = os.path.expanduser('~') + + if filename!=None: + self.bufferOpen(filename) + + self.Bind(wx.EVT_IDLE, self.OnIdle) + + def OnClose(self, event): + """Event handler for closing.""" + self.bufferClose() + + def OnAbout(self, event): + """Display an About window.""" + title = 'About PySlices' + text = 'PySlices %s\n\n' % VERSION + \ + 'Yet another Python shell, only flakier.\n\n' + \ + 'Half-baked by Patrick K. O\'Brien,\n' + \ + 'the other half is still in the oven.\n\n' + \ + 'Shell Revision: %s\n' % self.sliceshell.revision + \ + 'Interpreter Revision: %s\n\n' % self.sliceshell.interp.revision + \ + 'Platform: %s\n' % sys.platform + \ + 'Python Version: %s\n' % sys.version.split()[0] + \ + 'wxPython Version: %s\n' % wx.VERSION_STRING + \ + ('\t(%s)\n' % ", ".join(wx.PlatformInfo[1:])) + dialog = wx.MessageDialog(self, text, title, + wx.OK | wx.ICON_INFORMATION) + dialog.ShowModal() + dialog.Destroy() + + def OnEnableShellMode(self,event): + """Change between Slices Mode and Shell Mode""" + frame.Frame.OnEnableShellMode(self,event) + self.sliceshell.ToggleShellMode(self.enableShellMode) + + def OnHideFoldingMargin(self,event): + """Change between Slices Mode and Shell Mode""" + frame.Frame.OnHideFoldingMargin(self,event) + self.sliceshell.ToggleFoldingMargin(self.hideFoldingMargin) + + # Stolen Straight from editor.EditorFrame + # Modified a little... :) + # || + # \/ + def OnIdle(self, event): + """Event handler for idle time.""" + self._updateTitle() + event.Skip() + + def _updateTitle(self): + """Show current title information.""" + title = self.GetTitle() + if self.bufferHasChanged(): + if title.startswith('* '): + pass + else: + self.SetTitle('* ' + title) + else: + if title.startswith('* '): + self.SetTitle(title[2:]) + + def hasBuffer(self): + """Return True if there is a current buffer.""" + if self.buffer: + return True + else: + return False + + def bufferClose(self): + """Close buffer.""" + if self.buffer.hasChanged(): + cancel = self.bufferSuggestSave() + if cancel: + #event.Veto() + return cancel + self.SaveSettings() + self.crust.sliceshell.destroy() + self.bufferDestroy() + self.Destroy() + + return False + + def bufferCreate(self, filename=None): + """Create new buffer.""" + self.bufferDestroy() + buffer = Buffer() + self.panel = panel = wx.Panel(parent=self, id=-1) + panel.Bind (wx.EVT_ERASE_BACKGROUND, lambda x: x) + editor = Editor(parent=panel) + panel.editor = editor + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(editor.window, 1, wx.EXPAND) + panel.SetSizer(sizer) + panel.SetAutoLayout(True) + sizer.Layout() + buffer.addEditor(editor) + buffer.open(filename) + self.setEditor(editor) + self.editor.setFocus() + self.SendSizeEvent() + + + def bufferDestroy(self): + """Destroy the current buffer.""" + if self.buffer: + self.editor = None + self.buffer = None + + + def bufferHasChanged(self): + """Return True if buffer has changed since last save.""" + if self.buffer: + return self.buffer.hasChanged() + else: + return False + + def bufferNew(self): + """Create new buffer.""" + cancel = self.bufferSuggestSave() + if cancel: + return cancel + self.sliceshell.clear() + self.SetTitle( 'PySlices') + self.sliceshell.NeedsCheckForSave=False + self.sliceshell.SetSavePoint() + self.buffer.doc = document.Document() + self.buffer.name = 'This shell' + self.buffer.modulename = self.buffer.doc.filebase + #self.bufferCreate() + cancel = False + return cancel + + def bufferOpen(self,file=None): + """Open file in buffer.""" + if self.bufferHasChanged(): + cancel = self.bufferSuggestSave() + if cancel: + return cancel + + if file==None: + file=wx.FileSelector('Open a PySlices File', + wildcard='*.pyslices', + default_path=self.currentDirectory) + if file!=None and file!=u'': + fid=open(file,'r') + self.sliceshell.LoadPySlicesFile(fid) + fid.close() + self.currentDirectory = os.path.split(file)[0] + self.SetTitle( os.path.split(file)[1] + ' - PySlices') + self.sliceshell.NeedsCheckForSave=False + self.sliceshell.SetSavePoint() + self.buffer.doc = document.Document(file) + self.buffer.name = self.buffer.doc.filename + self.buffer.modulename = self.buffer.doc.filebase + self.sliceshell.ScrollToLine(0) + return + +## def bufferPrint(self): +## """Print buffer.""" +## pass + +## def bufferRevert(self): +## """Revert buffer to version of file on disk.""" +## pass + + # was self.buffer.save(self): # """Save buffer.""" + def simpleSave(self,confirmed=False): + filepath = self.buffer.doc.filepath + self.buffer.confirmed = confirmed + if not filepath: + return # XXX Get filename + if not os.path.exists(filepath): + self.buffer.confirmed = True + if not self.buffer.confirmed: + self.buffer.confirmed = self.buffer.overwriteConfirm(filepath) + if self.buffer.confirmed: + try: + fid = open(filepath, 'wb') + self.sliceshell.SavePySlicesFile(fid) + finally: + if fid: + fid.close() + self.sliceshell.SetSavePoint() + self.SetTitle( os.path.split(filepath)[1] + ' - PySlices') + self.sliceshell.NeedsCheckForSave=False + + def bufferSave(self): + """Save buffer to its file.""" + if self.buffer.doc.filepath: + # self.buffer.save() + self.simpleSave(confirmed=True) + cancel = False + else: + cancel = self.bufferSaveAs() + return cancel + + def bufferSaveAs(self): + """Save buffer to a new filename.""" + if self.bufferHasChanged() and self.buffer.doc.filepath: + cancel = self.bufferSuggestSave() + if cancel: + return cancel + filedir = '' + if self.buffer and self.buffer.doc.filedir: + filedir = self.buffer.doc.filedir + result = editor.saveSingle(title='Save PySlices File',directory=filedir, + wildcard='PySlices Files (*.pyslices)|*.pyslices') + if result.path not in ['',None]: + if result.path[-9:]!=".pyslices": + result.path+=".pyslices" + + self.buffer.doc = document.Document(result.path) + self.buffer.name = self.buffer.doc.filename + self.buffer.modulename = self.buffer.doc.filebase + self.simpleSave(confirmed=True) # allow overwrite + cancel = False + else: + cancel = True + return cancel + + def bufferSaveACopy(self): + """Save buffer to a new filename.""" + filedir = '' + if self.buffer and self.buffer.doc.filedir: + filedir = self.buffer.doc.filedir + result = editor.saveSingle(title='Save a Copy of PySlices File',directory=filedir, + wildcard='PySlices Files (*.pyslices)|*.pyslices') + + if result.path not in ['',None]: + if result.path[-9:]!=".pyslices": + result.path+=".pyslices" + + # if not os.path.exists(result.path): + try: # Allow overwrite... + fid = open(result.path, 'wb') + self.sliceshell.SavePySlicesFile(fid) + finally: + if fid: + fid.close() + + cancel = False + else: + cancel = True + return cancel + + def bufferSuggestSave(self): + """Suggest saving changes. Return True if user selected Cancel.""" + result = editor.messageDialog(parent=None, + message='%s has changed.\n' + 'Would you like to save it first' + '?' % self.buffer.name, + title='Save current file?', + style=wx.YES_NO | wx.CANCEL | wx.NO_DEFAULT | + wx.CENTRE | wx.ICON_QUESTION ) + if result.positive: + cancel = self.bufferSave() + else: + cancel = result.text == 'Cancel' + return cancel + + def updateNamespace(self): + """Update the buffer namespace for autocompletion and calltips.""" + if self.buffer.updateNamespace(): + self.SetStatusText('Namespace updated') + else: + self.SetStatusText('Error executing, unable to update namespace') diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/dispatcher.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/dispatcher.py new file mode 100644 index 0000000..c2a4673 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/dispatcher.py @@ -0,0 +1,262 @@ +"""Provides global signal dispatching services.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import exceptions +import types +import weakref + + +class DispatcherError(exceptions.Exception): + def __init__(self, args=None): + self.args = args + + +class Parameter: + """Used to represent default parameter values.""" + def __repr__(self): + return self.__class__.__name__ + +class Any(Parameter): pass +Any = Any() + +class Anonymous(Parameter): pass +Anonymous = Anonymous() + + +connections = {} +senders = {} +_boundMethods = weakref.WeakKeyDictionary() + + +def connect(receiver, signal=Any, sender=Any, weak=True): + """ + Connect receiver to sender for signal. + + * If sender is Any, receiver will receive signal from any sender. + * If signal is Any, receiver will receive any signal from sender. + * If sender is None, receiver will receive signal from Anonymous. + * If signal is Any and sender is None, receiver will receive any + signal from Anonymous. + * If signal is Any and sender is Any, receiver will receive any + signal from any sender. + * If weak is true, weak references will be used. + """ + if signal is None: + raise DispatcherError, 'signal cannot be None' + if weak: + receiver = safeRef(receiver) + senderkey = id(sender) + signals = {} + if connections.has_key(senderkey): + signals = connections[senderkey] + else: + connections[senderkey] = signals + # Keep track of senders for cleanup. + if sender not in (None, Any): + def remove(object, senderkey=senderkey): + _removeSender(senderkey=senderkey) + # Skip objects that can not be weakly referenced, which means + # they won't be automatically cleaned up, but that's too bad. + try: + weakSender = weakref.ref(sender, remove) + senders[senderkey] = weakSender + except: + pass + receivers = [] + if signals.has_key(signal): + receivers = signals[signal] + else: + signals[signal] = receivers + try: + receivers.remove(receiver) + except ValueError: + pass + receivers.append(receiver) + +def disconnect(receiver, signal=Any, sender=Any, weak=True): + """Disconnect receiver from sender for signal. + + Disconnecting is not required. The use of disconnect is the same as for + connect, only in reverse. Think of it as undoing a previous connection.""" + if signal is None: + raise DispatcherError, 'signal cannot be None' + if weak: + receiver = safeRef(receiver) + senderkey = id(sender) + try: + receivers = connections[senderkey][signal] + except KeyError: + raise DispatcherError, \ + 'No receivers for signal %r from sender %s' % (signal, sender) + try: + receivers.remove(receiver) + except ValueError: + raise DispatcherError, \ + 'No connection to receiver %s for signal %r from sender %s' % \ + (receiver, signal, sender) + _cleanupConnections(senderkey, signal) + +def send(signal, sender=Anonymous, **kwds): + """Send signal from sender to all connected receivers. + + Return a list of tuple pairs [(receiver, response), ... ]. + If sender is not specified, signal is sent anonymously.""" + senderkey = id(sender) + anykey = id(Any) + # Get receivers that receive *this* signal from *this* sender. + receivers = [] + try: + receivers.extend(connections[senderkey][signal]) + except KeyError: + pass + # Add receivers that receive *any* signal from *this* sender. + anyreceivers = [] + try: + anyreceivers = connections[senderkey][Any] + except KeyError: + pass + for receiver in anyreceivers: + if receivers.count(receiver) == 0: + receivers.append(receiver) + # Add receivers that receive *this* signal from *any* sender. + anyreceivers = [] + try: + anyreceivers = connections[anykey][signal] + except KeyError: + pass + for receiver in anyreceivers: + if receivers.count(receiver) == 0: + receivers.append(receiver) + # Add receivers that receive *any* signal from *any* sender. + anyreceivers = [] + try: + anyreceivers = connections[anykey][Any] + except KeyError: + pass + for receiver in anyreceivers: + if receivers.count(receiver) == 0: + receivers.append(receiver) + # Call each receiver with whatever arguments it can accept. + # Return a list of tuple pairs [(receiver, response), ... ]. + responses = [] + for receiver in receivers: + if type(receiver) is weakref.ReferenceType \ + or isinstance(receiver, BoundMethodWeakref): + # Dereference the weak reference. + receiver = receiver() + if receiver is None: + # This receiver is dead, so skip it. + continue + response = _call(receiver, signal=signal, sender=sender, **kwds) + responses += [(receiver, response)] + return responses + +def _call(receiver, **kwds): + """Call receiver with only arguments it can accept.""" +## if type(receiver) is types.InstanceType: + if hasattr(receiver, '__call__') and \ + (hasattr(receiver.__call__, 'im_func') or hasattr(receiver.__call__, 'im_code')): + # receiver is a class instance; assume it is callable. + # Reassign receiver to the actual method that will be called. + receiver = receiver.__call__ + if hasattr(receiver, 'im_func'): + # receiver is a method. Drop the first argument, usually 'self'. + fc = receiver.im_func.func_code + acceptable = fc.co_varnames[1:fc.co_argcount] + elif hasattr(receiver, 'func_code'): + # receiver is a function. + fc = receiver.func_code + acceptable = fc.co_varnames[0:fc.co_argcount] + else: + raise DispatcherError, 'Unknown receiver %s of type %s' % (receiver, type(receiver)) + if not (fc.co_flags & 8): + # fc does not have a **kwds type parameter, therefore + # remove unacceptable arguments. + for arg in kwds.keys(): + if arg not in acceptable: + del kwds[arg] + return receiver(**kwds) + + +def safeRef(object): + """Return a *safe* weak reference to a callable object.""" + if hasattr(object, 'im_self'): + if object.im_self is not None: + # Turn a bound method into a BoundMethodWeakref instance. + # Keep track of these instances for lookup by disconnect(). + selfkey = object.im_self + funckey = object.im_func + if not _boundMethods.has_key(selfkey): + _boundMethods[selfkey] = weakref.WeakKeyDictionary() + if not _boundMethods[selfkey].has_key(funckey): + _boundMethods[selfkey][funckey] = \ + BoundMethodWeakref(boundMethod=object) + return _boundMethods[selfkey][funckey] + return weakref.ref(object, _removeReceiver) + + +class BoundMethodWeakref: + """BoundMethodWeakref class.""" + + def __init__(self, boundMethod): + """Return a weak-reference-like instance for a bound method.""" + self.isDead = 0 + def remove(object, self=self): + """Set self.isDead to true when method or instance is destroyed.""" + self.isDead = 1 + _removeReceiver(receiver=self) + self.weakSelf = weakref.ref(boundMethod.im_self, remove) + self.weakFunc = weakref.ref(boundMethod.im_func, remove) + + def __repr__(self): + """Return the closest representation.""" + return '<bound method weakref for %s.%s>' % (self.weakSelf, self.weakFunc) + + def __call__(self): + """Return a strong reference to the bound method.""" + if self.isDead: + return None + else: + object = self.weakSelf() + method = self.weakFunc().__name__ + try: # wxPython hack to handle wxDead objects. + return getattr(object, method) + except AttributeError: +## _removeReceiver(receiver=self) + return None + + +def _removeReceiver(receiver): + """Remove receiver from connections.""" + for senderkey in connections.keys(): + for signal in connections[senderkey].keys(): + receivers = connections[senderkey][signal] + try: + receivers.remove(receiver) + except: + pass + _cleanupConnections(senderkey, signal) + +def _cleanupConnections(senderkey, signal): + """Delete any empty signals for senderkey. Delete senderkey if empty.""" + receivers = connections[senderkey][signal] + if not receivers: + # No more connected receivers. Therefore, remove the signal. + signals = connections[senderkey] + del signals[signal] + if not signals: + # No more signal connections. Therefore, remove the sender. + _removeSender(senderkey) + +def _removeSender(senderkey): + """Remove senderkey from connections.""" + del connections[senderkey] + # Senderkey will only be in senders dictionary if sender + # could be weakly referenced. + try: + del senders[senderkey] + except: + pass diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/document.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/document.py new file mode 100644 index 0000000..ab00ca0 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/document.py @@ -0,0 +1,43 @@ +"""Document class.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import os + + +class Document: + """Document class.""" + + def __init__(self, filename=None): + """Create a Document instance.""" + self.filename = filename + self.filepath = None + self.filedir = None + self.filebase = None + self.fileext = None + if self.filename: + self.filepath = os.path.realpath(self.filename) + self.filedir, self.filename = os.path.split(self.filepath) + self.filebase, self.fileext = os.path.splitext(self.filename) + + def read(self): + """Return contents of file.""" + if self.filepath and os.path.exists(self.filepath): + f = file(self.filepath, 'rb') + try: + return f.read() + finally: + f.close() + else: + return '' + + def write(self, text): + """Write text to file.""" + try: + f = file(self.filepath, 'wb') + f.write(text) + finally: + if f: + f.close() diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/editor.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/editor.py new file mode 100644 index 0000000..8c66de8 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/editor.py @@ -0,0 +1,838 @@ +"""PyAlaCarte and PyAlaMode editors.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import wx + +from buffer import Buffer +import crust +import dispatcher +import editwindow +import frame +from shell import Shell +import version + + +class EditorFrame(frame.Frame): + """Frame containing one editor.""" + + def __init__(self, parent=None, id=-1, title='PyAlaCarte', + pos=wx.DefaultPosition, size=(800, 600), + style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE, + filename=None): + """Create EditorFrame instance.""" + frame.Frame.__init__(self, parent, id, title, pos, size, style) + self.buffers = {} + self.buffer = None # Current buffer. + self.editor = None + self._defaultText = title + ' - the tastiest Python editor.' + self._statusText = self._defaultText + self.SetStatusText(self._statusText) + self.Bind(wx.EVT_IDLE, self.OnIdle) + self._setup() + if filename: + self.bufferCreate(filename) + + def _setup(self): + """Setup prior to first buffer creation. + + Useful for subclasses.""" + pass + + def setEditor(self, editor): + self.editor = editor + self.buffer = self.editor.buffer + self.buffers[self.buffer.id] = self.buffer + + def OnAbout(self, event): + """Display an About window.""" + title = 'About PyAlaCarte' + text = 'Another fine, flaky program.' + dialog = wx.MessageDialog(self, text, title, + wx.OK | wx.ICON_INFORMATION) + dialog.ShowModal() + dialog.Destroy() + + def OnClose(self, event): + """Event handler for closing.""" + for buffer in self.buffers.values(): + self.buffer = buffer + if buffer.hasChanged(): + cancel = self.bufferSuggestSave() + if cancel and event.CanVeto(): + event.Veto() + return + self.Destroy() + + def OnIdle(self, event): + """Event handler for idle time.""" + self._updateStatus() + if hasattr(self, 'notebook'): + self._updateTabText() + self._updateTitle() + event.Skip() + + def _updateStatus(self): + """Show current status information.""" + if self.editor and hasattr(self.editor, 'getStatus'): + status = self.editor.getStatus() + text = 'File: %s | Line: %d | Column: %d' % status + else: + text = self._defaultText + if text != self._statusText: + self.SetStatusText(text) + self._statusText = text + + def _updateTabText(self): + """Show current buffer information on notebook tab.""" +## suffix = ' **' +## notebook = self.notebook +## selection = notebook.GetSelection() +## if selection == -1: +## return +## text = notebook.GetPageText(selection) +## window = notebook.GetPage(selection) +## if window.editor and window.editor.buffer.hasChanged(): +## if text.endswith(suffix): +## pass +## else: +## notebook.SetPageText(selection, text + suffix) +## else: +## if text.endswith(suffix): +## notebook.SetPageText(selection, text[:len(suffix)]) + + def _updateTitle(self): + """Show current title information.""" + title = self.GetTitle() + if self.bufferHasChanged(): + if title.startswith('* '): + pass + else: + self.SetTitle('* ' + title) + else: + if title.startswith('* '): + self.SetTitle(title[2:]) + + def hasBuffer(self): + """Return True if there is a current buffer.""" + if self.buffer: + return True + else: + return False + + def bufferClose(self): + """Close buffer.""" + if self.bufferHasChanged(): + cancel = self.bufferSuggestSave() + if cancel: + return cancel + self.bufferDestroy() + cancel = False + return cancel + + def bufferCreate(self, filename=None): + """Create new buffer.""" + self.bufferDestroy() + buffer = Buffer() + self.panel = panel = wx.Panel(parent=self, id=-1) + panel.Bind (wx.EVT_ERASE_BACKGROUND, lambda x: x) + editor = Editor(parent=panel) + panel.editor = editor + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(editor.window, 1, wx.EXPAND) + panel.SetSizer(sizer) + panel.SetAutoLayout(True) + sizer.Layout() + buffer.addEditor(editor) + buffer.open(filename) + self.setEditor(editor) + self.editor.setFocus() + self.SendSizeEvent() + + + def bufferDestroy(self): + """Destroy the current buffer.""" + if self.buffer: + for editor in self.buffer.editors.values(): + editor.destroy() + self.editor = None + del self.buffers[self.buffer.id] + self.buffer = None + self.panel.Destroy() + + + def bufferHasChanged(self): + """Return True if buffer has changed since last save.""" + if self.buffer: + return self.buffer.hasChanged() + else: + return False + + def bufferNew(self): + """Create new buffer.""" + if self.bufferHasChanged(): + cancel = self.bufferSuggestSave() + if cancel: + return cancel + self.bufferCreate() + cancel = False + return cancel + + def bufferOpen(self): + """Open file in buffer.""" + if self.bufferHasChanged(): + cancel = self.bufferSuggestSave() + if cancel: + return cancel + filedir = '' + if self.buffer and self.buffer.doc.filedir: + filedir = self.buffer.doc.filedir + result = openSingle(directory=filedir) + if result.path: + self.bufferCreate(result.path) + cancel = False + return cancel + +## def bufferPrint(self): +## """Print buffer.""" +## pass + +## def bufferRevert(self): +## """Revert buffer to version of file on disk.""" +## pass + + def bufferSave(self): + """Save buffer to its file.""" + if self.buffer.doc.filepath: + self.buffer.save() + cancel = False + else: + cancel = self.bufferSaveAs() + return cancel + + def bufferSaveAs(self): + """Save buffer to a new filename.""" + if self.bufferHasChanged() and self.buffer.doc.filepath: + cancel = self.bufferSuggestSave() + if cancel: + return cancel + filedir = '' + if self.buffer and self.buffer.doc.filedir: + filedir = self.buffer.doc.filedir + result = saveSingle(directory=filedir) + if result.path: + self.buffer.saveAs(result.path) + cancel = False + else: + cancel = True + return cancel + + def bufferSuggestSave(self): + """Suggest saving changes. Return True if user selected Cancel.""" + result = messageDialog(parent=None, + message='%s has changed.\n' + 'Would you like to save it first' + '?' % self.buffer.name, + title='Save current file?') + if result.positive: + cancel = self.bufferSave() + else: + cancel = result.text == 'Cancel' + return cancel + + def updateNamespace(self): + """Update the buffer namespace for autocompletion and calltips.""" + if self.buffer.updateNamespace(): + self.SetStatusText('Namespace updated') + else: + self.SetStatusText('Error executing, unable to update namespace') + + +class EditorNotebookFrame(EditorFrame): + """Frame containing one or more editors in a notebook.""" + + def __init__(self, parent=None, id=-1, title='PyAlaMode', + pos=wx.DefaultPosition, size=(800, 600), + style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE, + filename=None): + """Create EditorNotebookFrame instance.""" + self.notebook = None + EditorFrame.__init__(self, parent, id, title, pos, + size, style, filename) + if self.notebook: + dispatcher.connect(receiver=self._editorChange, + signal='EditorChange', sender=self.notebook) + + def _setup(self): + """Setup prior to first buffer creation. + + Called automatically by base class during init.""" + self.notebook = EditorNotebook(parent=self) + intro = 'Py %s' % version.VERSION + import imp + module = imp.new_module('__main__') + import __builtin__ + module.__dict__['__builtins__'] = __builtin__ + namespace = module.__dict__.copy() + self.crust = crust.Crust(parent=self.notebook, intro=intro, locals=namespace) + self.shell = self.crust.shell + # Override the filling so that status messages go to the status bar. + self.crust.filling.tree.setStatusText = self.SetStatusText + # Override the shell so that status messages go to the status bar. + self.shell.setStatusText = self.SetStatusText + # Fix a problem with the sash shrinking to nothing. + self.crust.filling.SetSashPosition(200) + self.notebook.AddPage(page=self.crust, text='*Shell*', select=True) + self.setEditor(self.crust.editor) + self.crust.editor.SetFocus() + + def _editorChange(self, editor): + """Editor change signal receiver.""" + self.setEditor(editor) + + def OnAbout(self, event): + """Display an About window.""" + title = 'About PyAlaMode' + text = 'Another fine, flaky program.' + dialog = wx.MessageDialog(self, text, title, + wx.OK | wx.ICON_INFORMATION) + dialog.ShowModal() + dialog.Destroy() + + def _updateTitle(self): + """Show current title information.""" + pass +## title = self.GetTitle() +## if self.bufferHasChanged(): +## if title.startswith('* '): +## pass +## else: +## self.SetTitle('* ' + title) +## else: +## if title.startswith('* '): +## self.SetTitle(title[2:]) + + def bufferCreate(self, filename=None): + """Create new buffer.""" + buffer = Buffer() + panel = wx.Panel(parent=self.notebook, id=-1) + panel.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: x) + editor = Editor(parent=panel) + panel.editor = editor + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(editor.window, 1, wx.EXPAND) + panel.SetSizer(sizer) + panel.SetAutoLayout(True) + sizer.Layout() + buffer.addEditor(editor) + buffer.open(filename) + self.setEditor(editor) + self.notebook.AddPage(page=panel, text=self.buffer.name, select=True) + self.editor.setFocus() + + def bufferDestroy(self): + """Destroy the current buffer.""" + selection = self.notebook.GetSelection() +## print "Destroy Selection:", selection + if selection > 0: # Don't destroy the PyCrust tab. + if self.buffer: + del self.buffers[self.buffer.id] + self.buffer = None # Do this before DeletePage(). + self.notebook.DeletePage(selection) + + def bufferNew(self): + """Create new buffer.""" + self.bufferCreate() + cancel = False + return cancel + + def bufferOpen(self): + """Open file in buffer.""" + filedir = '' + if self.buffer and self.buffer.doc.filedir: + filedir = self.buffer.doc.filedir + result = openMultiple(directory=filedir) + for path in result.paths: + self.bufferCreate(path) + cancel = False + return cancel + + +class EditorNotebook(wx.Notebook): + """A notebook containing a page for each editor.""" + + def __init__(self, parent): + """Create EditorNotebook instance.""" + wx.Notebook.__init__(self, parent, id=-1, style=wx.CLIP_CHILDREN) + self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self.OnPageChanging, id=self.GetId()) + self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged, id=self.GetId()) + self.Bind(wx.EVT_IDLE, self.OnIdle) + + def OnIdle(self, event): + """Event handler for idle time.""" + self._updateTabText() + event.Skip() + + def _updateTabText(self): + """Show current buffer display name on all but first tab.""" + size = 3 + changed = ' **' + unchanged = ' --' + selection = self.GetSelection() + if selection < 1: + return + text = self.GetPageText(selection) + window = self.GetPage(selection) + if not window.editor: + return + if text.endswith(changed) or text.endswith(unchanged): + name = text[:-size] + else: + name = text + if name != window.editor.buffer.name: + text = window.editor.buffer.name + if window.editor.buffer.hasChanged(): + if text.endswith(changed): + text = None + elif text.endswith(unchanged): + text = text[:-size] + changed + else: + text += changed + else: + if text.endswith(changed): + text = text[:-size] + unchanged + elif text.endswith(unchanged): + text = None + else: + text += unchanged + if text is not None: + self.SetPageText(selection, text) + self.Refresh() # Needed on Win98. + + def OnPageChanging(self, event): + """Page changing event handler.""" + event.Skip() + + def OnPageChanged(self, event): + """Page changed event handler.""" + new = event.GetSelection() + window = self.GetPage(new) + dispatcher.send(signal='EditorChange', sender=self, + editor=window.editor) + window.SetFocus() + event.Skip() + + +class EditorShellNotebookFrame(EditorNotebookFrame): + """Frame containing a notebook containing EditorShellNotebooks.""" + + def __init__(self, parent=None, id=-1, title='PyAlaModeTest', + pos=wx.DefaultPosition, size=(600, 400), + style=wx.DEFAULT_FRAME_STYLE, + filename=None, singlefile=False): + """Create EditorShellNotebookFrame instance.""" + self._singlefile = singlefile + EditorNotebookFrame.__init__(self, parent, id, title, pos, + size, style, filename) + + def _setup(self): + """Setup prior to first buffer creation. + + Called automatically by base class during init.""" + if not self._singlefile: + self.notebook = EditorNotebook(parent=self) + + def OnAbout(self, event): + """Display an About window.""" + title = 'About PyAlaModePlus' + text = 'Another fine, flaky program.' + dialog = wx.MessageDialog(self, text, title, + wx.OK | wx.ICON_INFORMATION) + dialog.ShowModal() + dialog.Destroy() + + def bufferCreate(self, filename=None): + """Create new buffer.""" + if self._singlefile: + self.bufferDestroy() + notebook = EditorShellNotebook(parent=self, + filename=filename) + self.notebook = notebook + else: + notebook = EditorShellNotebook(parent=self.notebook, + filename=filename) + self.setEditor(notebook.editor) + if not self._singlefile: + self.notebook.AddPage(page=notebook, text=self.buffer.name, + select=True) + self.editor.setFocus() + + def bufferDestroy(self): + """Destroy the current buffer.""" + if self.buffer: + self.editor = None + del self.buffers[self.buffer.id] + self.buffer = None # Do this before DeletePage(). + if self._singlefile: + self.notebook.Destroy() + self.notebook = None + else: + selection = self.notebook.GetSelection() +## print "Destroy Selection:", selection + self.notebook.DeletePage(selection) + + def bufferNew(self): + """Create new buffer.""" + if self._singlefile and self.bufferHasChanged(): + cancel = self.bufferSuggestSave() + if cancel: + return cancel + self.bufferCreate() + cancel = False + return cancel + + def bufferOpen(self): + """Open file in buffer.""" + if self._singlefile and self.bufferHasChanged(): + cancel = self.bufferSuggestSave() + if cancel: + return cancel + filedir = '' + if self.buffer and self.buffer.doc.filedir: + filedir = self.buffer.doc.filedir + if self._singlefile: + result = openSingle(directory=filedir) + if result.path: + self.bufferCreate(result.path) + else: + result = openMultiple(directory=filedir) + for path in result.paths: + self.bufferCreate(path) + cancel = False + return cancel + + +class EditorShellNotebook(wx.Notebook): + """A notebook containing an editor page and a shell page.""" + + def __init__(self, parent, filename=None): + """Create EditorShellNotebook instance.""" + wx.Notebook.__init__(self, parent, id=-1) + usePanels = True + if usePanels: + editorparent = editorpanel = wx.Panel(self, -1) + shellparent = shellpanel = wx.Panel(self, -1) + else: + editorparent = self + shellparent = self + self.buffer = Buffer() + self.editor = Editor(parent=editorparent) + self.buffer.addEditor(self.editor) + self.buffer.open(filename) + self.shell = Shell(parent=shellparent, locals=self.buffer.interp.locals, + style=wx.CLIP_CHILDREN | wx.SUNKEN_BORDER) + self.buffer.interp.locals.clear() + if usePanels: + self.AddPage(page=editorpanel, text='Editor', select=True) + self.AddPage(page=shellpanel, text='Shell') + # Setup sizers + editorsizer = wx.BoxSizer(wx.VERTICAL) + editorsizer.Add(self.editor.window, 1, wx.EXPAND) + editorpanel.SetSizer(editorsizer) + editorpanel.SetAutoLayout(True) + shellsizer = wx.BoxSizer(wx.VERTICAL) + shellsizer.Add(self.shell, 1, wx.EXPAND) + shellpanel.SetSizer(shellsizer) + shellpanel.SetAutoLayout(True) + else: + self.AddPage(page=self.editor.window, text='Editor', select=True) + self.AddPage(page=self.shell, text='Shell') + self.editor.setFocus() + self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged, id=self.GetId()) + + def OnPageChanged(self, event): + """Page changed event handler.""" + selection = event.GetSelection() + if selection == 0: + self.editor.setFocus() + else: + self.shell.SetFocus() + event.Skip() + + def SetFocus(self): + wx.Notebook.SetFocus(self) + selection = self.GetSelection() + if selection == 0: + self.editor.setFocus() + else: + self.shell.SetFocus() + + +class Editor: + """Editor having an EditWindow.""" + + def __init__(self, parent, id=-1, pos=wx.DefaultPosition, + size=wx.DefaultSize, + style=wx.CLIP_CHILDREN | wx.SUNKEN_BORDER): + """Create Editor instance.""" + self.window = EditWindow(self, parent, id, pos, size, style) + self.id = self.window.GetId() + self.buffer = None + # Assign handlers for keyboard events. + self.window.Bind(wx.EVT_CHAR, self.OnChar) + self.window.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) + + def _setBuffer(self, buffer, text): + """Set the editor to a buffer. Private callback called by buffer.""" + self.buffer = buffer + self.autoCompleteKeys = buffer.interp.getAutoCompleteKeys() + self.clearAll() + self.setText(text) + self.emptyUndoBuffer() + self.setSavePoint() + + def destroy(self): + """Destroy all editor objects.""" + self.window.Destroy() + + def clearAll(self): + self.window.ClearAll() + + def emptyUndoBuffer(self): + self.window.EmptyUndoBuffer() + + def getStatus(self): + """Return (filepath, line, column) status tuple.""" + if self.window: + pos = self.window.GetCurrentPos() + line = self.window.LineFromPosition(pos) + 1 + col = self.window.GetColumn(pos) + if self.buffer: + name = self.buffer.doc.filepath or self.buffer.name + else: + name = '' + status = (name, line, col) + return status + else: + return ('', 0, 0) + + def getText(self): + """Return contents of editor.""" + return self.window.GetText() + + def hasChanged(self): + """Return True if contents have changed.""" + return self.window.GetModify() + + def setFocus(self): + """Set the input focus to the editor window.""" + self.window.SetFocus() + + def setSavePoint(self): + self.window.SetSavePoint() + + def setText(self, text): + """Set contents of editor.""" + self.window.SetText(text) + + def OnChar(self, event): + """Keypress event handler. + + Only receives an event if OnKeyDown calls event.Skip() for the + corresponding event.""" + + key = event.GetKeyCode() + if key in self.autoCompleteKeys: + # Usually the dot (period) key activates auto completion. + if self.window.AutoCompActive(): + self.window.AutoCompCancel() + self.window.ReplaceSelection('') + self.window.AddText(chr(key)) + text, pos = self.window.GetCurLine() + text = text[:pos] + if self.window.autoComplete: + self.autoCompleteShow(text) + elif key == ord('('): + # The left paren activates a call tip and cancels an + # active auto completion. + if self.window.AutoCompActive(): + self.window.AutoCompCancel() + self.window.ReplaceSelection('') + self.window.AddText('(') + text, pos = self.window.GetCurLine() + text = text[:pos] + self.autoCallTipShow(text) + else: + # Allow the normal event handling to take place. + event.Skip() + + def OnKeyDown(self, event): + """Key down event handler.""" + + key = event.GetKeyCode() + # If the auto-complete window is up let it do its thing. + if self.window.AutoCompActive(): + event.Skip() + return + controlDown = event.ControlDown() + altDown = event.AltDown() + shiftDown = event.ShiftDown() + # Let Ctrl-Alt-* get handled normally. + if controlDown and altDown: + event.Skip() + # Increase font size. + elif controlDown and key in (ord(']'),): + dispatcher.send(signal='FontIncrease') + # Decrease font size. + elif controlDown and key in (ord('['),): + dispatcher.send(signal='FontDecrease') + # Default font size. + elif controlDown and key in (ord('='),): + dispatcher.send(signal='FontDefault') + else: + event.Skip() + + def autoCompleteShow(self, command): + """Display auto-completion popup list.""" + list = self.buffer.interp.getAutoCompleteList(command, + includeMagic=self.window.autoCompleteIncludeMagic, + includeSingle=self.window.autoCompleteIncludeSingle, + includeDouble=self.window.autoCompleteIncludeDouble) + if list: + options = ' '.join(list) + offset = 0 + self.window.AutoCompShow(offset, options) + + def autoCallTipShow(self, command): + """Display argument spec and docstring in a popup window.""" + if self.window.CallTipActive(): + self.window.CallTipCancel() + (name, argspec, tip) = self.buffer.interp.getCallTip(command) + if tip: + dispatcher.send(signal='Shell.calltip', sender=self, calltip=tip) + if not self.window.autoCallTip: + return + startpos = self.window.GetCurrentPos() + if argspec: + self.window.AddText(argspec + ')') + endpos = self.window.GetCurrentPos() + self.window.SetSelection(startpos, endpos) + if tip: + tippos = startpos - (len(name) + 1) + fallback = startpos - self.GetColumn(startpos) + # In case there isn't enough room, only go back to the + # fallback. + tippos = max(tippos, fallback) + self.CallTipShow(tippos, tip) + + +class EditWindow(editwindow.EditWindow): + """EditWindow based on StyledTextCtrl.""" + + def __init__(self, editor, parent, id=-1, pos=wx.DefaultPosition, + size=wx.DefaultSize, + style=wx.CLIP_CHILDREN | wx.SUNKEN_BORDER): + """Create EditWindow instance.""" + editwindow.EditWindow.__init__(self, parent, id, pos, size, style) + self.editor = editor + + +class DialogResults: + """DialogResults class.""" + + def __init__(self, returned): + """Create wrapper for results returned by dialog.""" + self.returned = returned + self.positive = returned in (wx.ID_OK, wx.ID_YES) + self.text = self._asString() + + + def __repr__(self): + return str(self.__dict__) + + def _asString(self): + returned = self.returned + if returned == wx.ID_OK: + return "Ok" + elif returned == wx.ID_CANCEL: + return "Cancel" + elif returned == wx.ID_YES: + return "Yes" + elif returned == wx.ID_NO: + return "No" + + +def fileDialog(parent=None, title='Open', directory='', filename='', + wildcard='All Files (*.*)|*.*', + style=wx.OPEN | wx.MULTIPLE): + """File dialog wrapper function.""" + dialog = wx.FileDialog(parent, title, directory, filename, + wildcard, style) + result = DialogResults(dialog.ShowModal()) + if result.positive: + result.paths = dialog.GetPaths() + else: + result.paths = [] + dialog.Destroy() + return result + + +def openSingle(parent=None, title='Open', directory='', filename='', + wildcard='All Files (*.*)|*.*', style=wx.OPEN): + """File dialog wrapper function.""" + dialog = wx.FileDialog(parent, title, directory, filename, + wildcard, style) + result = DialogResults(dialog.ShowModal()) + if result.positive: + result.path = dialog.GetPath() + else: + result.path = None + dialog.Destroy() + return result + + +def openMultiple(parent=None, title='Open', directory='', filename='', + wildcard='All Files (*.*)|*.*', + style=wx.OPEN | wx.MULTIPLE): + """File dialog wrapper function.""" + return fileDialog(parent, title, directory, filename, wildcard, style) + + +def saveSingle(parent=None, title='Save', directory='', filename='', + wildcard='All Files (*.*)|*.*', + style=wx.SAVE | wx.OVERWRITE_PROMPT): + """File dialog wrapper function.""" + dialog = wx.FileDialog(parent, title, directory, filename, + wildcard, style) + result = DialogResults(dialog.ShowModal()) + if result.positive: + result.path = dialog.GetPath() + else: + result.path = None + dialog.Destroy() + return result + + +def directory(parent=None, message='Choose a directory', path='', style=0, + pos=wx.DefaultPosition, size=wx.DefaultSize): + """Dir dialog wrapper function.""" + dialog = wx.DirDialog(parent, message, path, style, pos, size) + result = DialogResults(dialog.ShowModal()) + if result.positive: + result.path = dialog.GetPath() + else: + result.path = None + dialog.Destroy() + return result + + +def messageDialog(parent=None, message='', title='Message box', + style=wx.YES_NO | wx.CANCEL | wx.CENTRE | wx.ICON_QUESTION, + pos=wx.DefaultPosition): + """Message dialog wrapper function.""" + dialog = wx.MessageDialog(parent, message, title, style, pos) + result = DialogResults(dialog.ShowModal()) + dialog.Destroy() + return result diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/editwindow.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/editwindow.py new file mode 100644 index 0000000..dadd003 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/editwindow.py @@ -0,0 +1,297 @@ +"""EditWindow class.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import wx +from wx import stc + +import keyword +import os +import sys +import time + +import dispatcher +from version import VERSION + + +if 'wxMSW' in wx.PlatformInfo: + FACES = { 'times' : 'Times New Roman', + 'mono' : 'Courier New', + 'helv' : 'Arial', + 'lucida' : 'Lucida Console', + 'other' : 'Comic Sans MS', + 'size' : 10, + 'lnsize' : 8, + 'backcol' : '#FFFFFF', + 'calltipbg' : '#FFFFB8', + 'calltipfg' : '#404040', + } + +elif 'wxGTK' in wx.PlatformInfo and ('gtk2' in wx.PlatformInfo or + 'gtk3' in wx.PlatformInfo): + FACES = { 'times' : 'Serif', + 'mono' : 'Monospace', + 'helv' : 'Sans', + 'other' : 'new century schoolbook', + 'size' : 10, + 'lnsize' : 9, + 'backcol' : '#FFFFFF', + 'calltipbg' : '#FFFFB8', + 'calltipfg' : '#404040', + } + +elif 'wxMac' in wx.PlatformInfo: + FACES = { 'times' : 'Lucida Grande', + 'mono' : 'Monaco', + 'helv' : 'Geneva', + 'other' : 'new century schoolbook', + 'size' : 12, + 'lnsize' : 10, + 'backcol' : '#FFFFFF', + 'calltipbg' : '#FFFFB8', + 'calltipfg' : '#404040', + } + +else: # GTK1, etc. + FACES = { 'times' : 'Times', + 'mono' : 'Courier', + 'helv' : 'Helvetica', + 'other' : 'new century schoolbook', + 'size' : 12, + 'lnsize' : 10, + 'backcol' : '#FFFFFF', + 'calltipbg' : '#FFFFB8', + 'calltipfg' : '#404040', + } + + +class EditWindow(stc.StyledTextCtrl): + """EditWindow based on StyledTextCtrl.""" + + revision = __revision__ + + def __init__(self, parent, id=-1, pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.CLIP_CHILDREN | wx.SUNKEN_BORDER): + """Create EditWindow instance.""" + stc.StyledTextCtrl.__init__(self, parent, id, pos, size, style) + self.__config() + stc.EVT_STC_UPDATEUI(self, id, self.OnUpdateUI) + dispatcher.connect(receiver=self._fontsizer, signal='FontIncrease') + dispatcher.connect(receiver=self._fontsizer, signal='FontDecrease') + dispatcher.connect(receiver=self._fontsizer, signal='FontDefault') + + def _fontsizer(self, signal): + """Receiver for Font* signals.""" + size = self.GetZoom() + if signal == 'FontIncrease': + size += 1 + elif signal == 'FontDecrease': + size -= 1 + elif signal == 'FontDefault': + size = 0 + self.SetZoom(size) + + + def __config(self): + self.setDisplayLineNumbers(False) + + self.SetLexer(stc.STC_LEX_PYTHON) + self.SetKeyWords(0, ' '.join(keyword.kwlist)) + + self.setStyles(FACES) + self.SetViewWhiteSpace(False) + self.SetTabWidth(4) + self.SetUseTabs(False) + # Do we want to automatically pop up command completion options? + self.autoComplete = True + self.autoCompleteIncludeMagic = True + self.autoCompleteIncludeSingle = True + self.autoCompleteIncludeDouble = True + self.autoCompleteCaseInsensitive = True + self.AutoCompSetIgnoreCase(self.autoCompleteCaseInsensitive) + self.autoCompleteAutoHide = False + self.AutoCompSetAutoHide(self.autoCompleteAutoHide) + self.AutoCompStops(' .,;:([)]}\'"\\<>%^&+-=*/|`') + # Do we want to automatically pop up command argument help? + self.autoCallTip = True + self.callTipInsert = True + self.CallTipSetBackground(FACES['calltipbg']) + self.CallTipSetForeground(FACES['calltipfg']) + self.SetWrapMode(False) + try: + self.SetEndAtLastLine(False) + except AttributeError: + pass + + def setDisplayLineNumbers(self, state): + self.lineNumbers = state + if state: + self.SetMarginType(1, stc.STC_MARGIN_NUMBER) + self.SetMarginWidth(1, 40) + else: + # Leave a small margin so the feature hidden lines marker can be seen + self.SetMarginType(1, 0) + self.SetMarginWidth(1, 10) + + def setStyles(self, faces): + """Configure font size, typeface and color for lexer.""" + + # Default style + self.StyleSetSpec(stc.STC_STYLE_DEFAULT, + "face:%(mono)s,size:%(size)d,back:%(backcol)s" % \ + faces) + + self.StyleClearAll() + self.SetSelForeground(True, wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)) + self.SetSelBackground(True, wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT)) + + # Built in styles + self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, + "back:#C0C0C0,face:%(mono)s,size:%(lnsize)d" % FACES) + self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, + "face:%(mono)s" % faces) + self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, + "fore:#0000FF,back:#FFFF88") + self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, + "fore:#FF0000,back:#FFFF88") + + # Python styles + self.StyleSetSpec(stc.STC_P_DEFAULT, + "face:%(mono)s" % faces) + self.StyleSetSpec(stc.STC_P_COMMENTLINE, + "fore:#007F00,face:%(mono)s" % faces) + self.StyleSetSpec(stc.STC_P_NUMBER, + "") + self.StyleSetSpec(stc.STC_P_STRING, + "fore:#7F007F,face:%(mono)s" % faces) + self.StyleSetSpec(stc.STC_P_CHARACTER, + "fore:#7F007F,face:%(mono)s" % faces) + self.StyleSetSpec(stc.STC_P_WORD, + "fore:#00007F,bold") + self.StyleSetSpec(stc.STC_P_TRIPLE, + "fore:#7F0000") + self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, + "fore:#000033,back:#FFFFE8") + self.StyleSetSpec(stc.STC_P_CLASSNAME, + "fore:#0000FF,bold") + self.StyleSetSpec(stc.STC_P_DEFNAME, + "fore:#007F7F,bold") + self.StyleSetSpec(stc.STC_P_OPERATOR, + "") + self.StyleSetSpec(stc.STC_P_IDENTIFIER, + "") + self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, + "fore:#7F7F7F") + self.StyleSetSpec(stc.STC_P_STRINGEOL, + "fore:#000000,face:%(mono)s,back:#E0C0E0,eolfilled" % faces) + + def OnUpdateUI(self, event): + """Check for matching braces.""" + # If the auto-complete window is up let it do its thing. + if self.AutoCompActive() or self.CallTipActive(): + return + braceAtCaret = -1 + braceOpposite = -1 + charBefore = None + caretPos = self.GetCurrentPos() + if caretPos > 0: + charBefore = self.GetCharAt(caretPos - 1) + styleBefore = self.GetStyleAt(caretPos - 1) + + # Check before. + if charBefore and chr(charBefore) in '[]{}()' \ + and styleBefore == stc.STC_P_OPERATOR: + braceAtCaret = caretPos - 1 + + # Check after. + if braceAtCaret < 0: + charAfter = self.GetCharAt(caretPos) + styleAfter = self.GetStyleAt(caretPos) + if charAfter and chr(charAfter) in '[]{}()' \ + and styleAfter == stc.STC_P_OPERATOR: + braceAtCaret = caretPos + + if braceAtCaret >= 0: + braceOpposite = self.BraceMatch(braceAtCaret) + + if braceAtCaret != -1 and braceOpposite == -1: + self.BraceBadLight(braceAtCaret) + else: + self.BraceHighlight(braceAtCaret, braceOpposite) + + def CanCopy(self): + """Return True if text is selected and can be copied.""" + return self.GetSelectionStart() != self.GetSelectionEnd() + + def CanCut(self): + """Return True if text is selected and can be cut.""" + return self.CanCopy() and self.CanEdit() + + def CanEdit(self): + """Return True if editing should succeed.""" + return not self.GetReadOnly() + + def CanPaste(self): + """Return True if pasting should succeed.""" + return stc.StyledTextCtrl.CanPaste(self) and self.CanEdit() + + + def GetLastPosition(self): + return self.GetLength() + + def GetRange(self, start, end): + return self.GetTextRange(start, end) + + def GetSelection(self): + return self.GetAnchor(), self.GetCurrentPos() + + def ShowPosition(self, pos): + line = self.LineFromPosition(pos) + #self.EnsureVisible(line) + self.GotoLine(line) + + def DoFindNext(self, findData, findDlg=None): + backward = not (findData.GetFlags() & wx.FR_DOWN) + matchcase = (findData.GetFlags() & wx.FR_MATCHCASE) != 0 + end = self.GetLastPosition() + # Changed to reflect the fact that StyledTextControl is in UTF-8 encoding + textstring = self.GetRange(0, end).encode('utf-8') + findstring = findData.GetFindString().encode('utf-8') + if not matchcase: + textstring = textstring.lower() + findstring = findstring.lower() + if backward: + start = self.GetSelection()[0] + loc = textstring.rfind(findstring, 0, start) + else: + start = self.GetSelection()[1] + loc = textstring.find(findstring, start) + + # if it wasn't found then restart at begining + if loc == -1 and start != 0: + if backward: + start = end + loc = textstring.rfind(findstring, 0, start) + else: + start = 0 + loc = textstring.find(findstring, start) + + # was it still not found? + if loc == -1: + dlg = wx.MessageDialog(self, 'Unable to find the search text.', + 'Not found!', + wx.OK | wx.ICON_INFORMATION) + dlg.ShowModal() + dlg.Destroy() + if findDlg: + if loc == -1: + wx.CallAfter(findDlg.SetFocus) + return + else: + findDlg.Close() + + # show and select the found text + self.ShowPosition(loc) + self.SetSelection(loc, loc + len(findstring)) diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/filling.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/filling.py new file mode 100644 index 0000000..4ffe662 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/filling.py @@ -0,0 +1,360 @@ +"""Filling is the gui tree control through which a user can navigate +the local namespace or any object.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import wx + +import dispatcher +import editwindow +import inspect +import introspect +import keyword +import sys +import types +from version import VERSION + + +COMMONTYPES = [getattr(types, t) for t in dir(types) \ + if not t.startswith('_') \ + and t not in ('ClassType', 'InstanceType', 'ModuleType')] + +DOCTYPES = ('BuiltinFunctionType', 'BuiltinMethodType', 'ClassType', + 'FunctionType', 'GeneratorType', 'InstanceType', + 'LambdaType', 'MethodType', 'ModuleType', + 'UnboundMethodType', 'method-wrapper') + +SIMPLETYPES = [getattr(types, t) for t in dir(types) \ + if not t.startswith('_') and t not in DOCTYPES] + +del t + +try: + COMMONTYPES.append(type(''.__repr__)) # Method-wrapper in version 2.2.x. +except AttributeError: + pass + + +class FillingTree(wx.TreeCtrl): + """FillingTree based on TreeCtrl.""" + + name = 'Filling Tree' + revision = __revision__ + + def __init__(self, parent, id=-1, pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.TR_DEFAULT_STYLE, + rootObject=None, rootLabel=None, rootIsNamespace=False, + static=False): + """Create FillingTree instance.""" + wx.TreeCtrl.__init__(self, parent, id, pos, size, style) + self.rootIsNamespace = rootIsNamespace + import __main__ + if rootObject is None: + rootObject = __main__.__dict__ + self.rootIsNamespace = True + if rootObject is __main__.__dict__ and rootLabel is None: + rootLabel = 'locals()' + if not rootLabel: + rootLabel = 'Ingredients' + rootData = wx.TreeItemData(rootObject) + self.item = self.root = self.AddRoot(rootLabel, -1, -1, rootData) + self.SetItemHasChildren(self.root, self.objHasChildren(rootObject)) + self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.OnItemExpanding, id=self.GetId()) + self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, id=self.GetId()) + self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=self.GetId()) + self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated, id=self.GetId()) + if not static: + dispatcher.connect(receiver=self.push, signal='Interpreter.push') + + def push(self, command, more): + """Receiver for Interpreter.push signal.""" + self.display() + + def OnItemExpanding(self, event): + """Add children to the item.""" + busy = wx.BusyCursor() + item = event.GetItem() + if self.IsExpanded(item): + return + self.addChildren(item) +# self.SelectItem(item) + + def OnItemCollapsed(self, event): + """Remove all children from the item.""" + busy = wx.BusyCursor() + item = event.GetItem() +# self.CollapseAndReset(item) +# self.DeleteChildren(item) +# self.SelectItem(item) + + def OnSelChanged(self, event): + """Display information about the item.""" + busy = wx.BusyCursor() + self.item = event.GetItem() + self.display() + + def OnItemActivated(self, event): + """Launch a DirFrame.""" + item = event.GetItem() + text = self.getFullName(item) + obj = self.GetPyData(item) + frame = FillingFrame(parent=self, size=(600, 100), rootObject=obj, + rootLabel=text, rootIsNamespace=False) + frame.Show() + + def objHasChildren(self, obj): + """Return true if object has children.""" + if self.objGetChildren(obj): + return True + else: + return False + + def objGetChildren(self, obj): + """Return dictionary with attributes or contents of object.""" + busy = wx.BusyCursor() + otype = type(obj) + if otype is types.DictType \ + or str(otype)[17:23] == 'BTrees' and hasattr(obj, 'keys'): + return obj + d = {} + if otype is types.ListType or otype is types.TupleType: + for n in range(len(obj)): + key = '[' + str(n) + ']' + d[key] = obj[n] + if otype not in COMMONTYPES: + for key in introspect.getAttributeNames(obj): + # Believe it or not, some attributes can disappear, + # such as the exc_traceback attribute of the sys + # module. So this is nested in a try block. + try: + d[key] = getattr(obj, key) + except: + pass + return d + + def addChildren(self, item): + self.DeleteChildren(item) + obj = self.GetPyData(item) + children = self.objGetChildren(obj) + if not children: + return + keys = children.keys() + keys.sort(lambda x, y: cmp(str(x).lower(), str(y).lower())) + for key in keys: + itemtext = str(key) + # Show string dictionary items with single quotes, except + # for the first level of items, if they represent a + # namespace. + if type(obj) is types.DictType \ + and type(key) is types.StringType \ + and (item != self.root \ + or (item == self.root and not self.rootIsNamespace)): + itemtext = repr(key) + child = children[key] + data = wx.TreeItemData(child) + branch = self.AppendItem(parent=item, text=itemtext, data=data) + self.SetItemHasChildren(branch, self.objHasChildren(child)) + + def display(self): + item = self.item + if not item: + return + if self.IsExpanded(item): + self.addChildren(item) + self.setText('') + obj = self.GetPyData(item) + if wx.Platform == '__WXMSW__': + if obj is None: # Windows bug fix. + return + self.SetItemHasChildren(item, self.objHasChildren(obj)) + otype = type(obj) + text = '' + text += self.getFullName(item) + text += '\n\nType: ' + str(otype) + try: + value = str(obj) + except: + value = '' + if otype is types.StringType or otype is types.UnicodeType: + value = repr(obj) + text += u'\n\nValue: ' + value + if otype not in SIMPLETYPES: + try: + text += '\n\nDocstring:\n\n"""' + \ + inspect.getdoc(obj).strip() + '"""' + except: + pass + if otype is types.InstanceType: + try: + text += '\n\nClass Definition:\n\n' + \ + inspect.getsource(obj.__class__) + except: + pass + else: + try: + text += '\n\nSource Code:\n\n' + \ + inspect.getsource(obj) + except: + pass + self.setText(text) + + def getFullName(self, item, partial=''): + """Return a syntactically proper name for item.""" + name = self.GetItemText(item) + parent = None + obj = None + if item != self.root: + parent = self.GetItemParent(item) + obj = self.GetPyData(parent) + # Apply dictionary syntax to dictionary items, except the root + # and first level children of a namepace. + if (type(obj) is types.DictType \ + or str(type(obj))[17:23] == 'BTrees' \ + and hasattr(obj, 'keys')) \ + and ((item != self.root and parent != self.root) \ + or (parent == self.root and not self.rootIsNamespace)): + name = '[' + name + ']' + # Apply dot syntax to multipart names. + if partial: + if partial[0] == '[': + name += partial + else: + name += '.' + partial + # Repeat for everything but the root item + # and first level children of a namespace. + if (item != self.root and parent != self.root) \ + or (parent == self.root and not self.rootIsNamespace): + name = self.getFullName(parent, partial=name) + return name + + def setText(self, text): + """Display information about the current selection.""" + + # This method will likely be replaced by the enclosing app to + # do something more interesting, like write to a text control. + print text + + def setStatusText(self, text): + """Display status information.""" + + # This method will likely be replaced by the enclosing app to + # do something more interesting, like write to a status bar. + print text + + +class FillingText(editwindow.EditWindow): + """FillingText based on StyledTextCtrl.""" + + name = 'Filling Text' + revision = __revision__ + + def __init__(self, parent, id=-1, pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.CLIP_CHILDREN, + static=False): + """Create FillingText instance.""" + editwindow.EditWindow.__init__(self, parent, id, pos, size, style) + # Configure various defaults and user preferences. + self.SetReadOnly(True) + self.SetWrapMode(True) + self.SetMarginWidth(1, 0) + if not static: + dispatcher.connect(receiver=self.push, signal='Interpreter.push') + + def push(self, command, more): + """Receiver for Interpreter.push signal.""" + self.Refresh() + + def SetText(self, *args, **kwds): + self.SetReadOnly(False) + editwindow.EditWindow.SetText(self, *args, **kwds) + self.SetReadOnly(True) + + +class Filling(wx.SplitterWindow): + """Filling based on wxSplitterWindow.""" + + name = 'Filling' + revision = __revision__ + + def __init__(self, parent, id=-1, pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.SP_3D|wx.SP_LIVE_UPDATE, + name='Filling Window', rootObject=None, + rootLabel=None, rootIsNamespace=False, static=False): + """Create a Filling instance.""" + wx.SplitterWindow.__init__(self, parent, id, pos, size, style, name) + + self.tree = FillingTree(parent=self, rootObject=rootObject, + rootLabel=rootLabel, + rootIsNamespace=rootIsNamespace, + static=static) + self.text = FillingText(parent=self, static=static) + + wx.FutureCall(1, self.SplitVertically, self.tree, self.text, 200) + + self.SetMinimumPaneSize(1) + + # Override the filling so that descriptions go to FillingText. + self.tree.setText = self.text.SetText + + # Display the root item. + self.tree.SelectItem(self.tree.root) + self.tree.display() + + self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnChanged) + + def OnChanged(self, event): + #this is important: do not evaluate this event=> otherwise, + # splitterwindow behaves strangely + #event.Skip() + pass + + + def LoadSettings(self, config): + pos = config.ReadInt('Sash/FillingPos', 200) + wx.FutureCall(250, self.SetSashPosition, pos) + zoom = config.ReadInt('View/Zoom/Filling', -99) + if zoom != -99: + self.text.SetZoom(zoom) + + def SaveSettings(self, config): + config.WriteInt('Sash/FillingPos', self.GetSashPosition()) + config.WriteInt('View/Zoom/Filling', self.text.GetZoom()) + + + +class FillingFrame(wx.Frame): + """Frame containing the namespace tree component.""" + + name = 'Filling Frame' + revision = __revision__ + + def __init__(self, parent=None, id=-1, title='PyFilling', + pos=wx.DefaultPosition, size=(600, 400), + style=wx.DEFAULT_FRAME_STYLE, rootObject=None, + rootLabel=None, rootIsNamespace=False, static=False): + """Create FillingFrame instance.""" + wx.Frame.__init__(self, parent, id, title, pos, size, style) + intro = 'PyFilling - The Tastiest Namespace Inspector' + self.CreateStatusBar() + self.SetStatusText(intro) + import images + self.SetIcon(images.getPyIcon()) + self.filling = Filling(parent=self, rootObject=rootObject, + rootLabel=rootLabel, + rootIsNamespace=rootIsNamespace, + static=static) + # Override so that status messages go to the status bar. + self.filling.tree.setStatusText = self.SetStatusText + + +class App(wx.App): + """PyFilling standalone application.""" + + def OnInit(self): + wx.InitAllImageHandlers() + self.fillingFrame = FillingFrame() + self.fillingFrame.Show(True) + self.SetTopWindow(self.fillingFrame) + return True diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/frame.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/frame.py new file mode 100644 index 0000000..2bece97 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/frame.py @@ -0,0 +1,984 @@ +"""Base frame with menu.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import wx +import os +from version import VERSION +import editwindow +import dispatcher + +ID_NEW = wx.ID_NEW +ID_OPEN = wx.ID_OPEN +ID_REVERT = wx.ID_REVERT +ID_CLOSE = wx.ID_CLOSE +ID_SAVE = wx.ID_SAVE +ID_SAVEAS = wx.ID_SAVEAS +ID_PRINT = wx.ID_PRINT +ID_EXIT = wx.ID_EXIT +ID_UNDO = wx.ID_UNDO +ID_REDO = wx.ID_REDO +ID_CUT = wx.ID_CUT +ID_COPY = wx.ID_COPY +ID_PASTE = wx.ID_PASTE +ID_CLEAR = wx.ID_CLEAR +ID_SELECTALL = wx.ID_SELECTALL +ID_EMPTYBUFFER = wx.NewId() +ID_ABOUT = wx.ID_ABOUT +ID_HELP = wx.NewId() +ID_AUTOCOMP = wx.NewId() +ID_AUTOCOMP_SHOW = wx.NewId() +ID_AUTOCOMP_MAGIC = wx.NewId() +ID_AUTOCOMP_SINGLE = wx.NewId() +ID_AUTOCOMP_DOUBLE = wx.NewId() +ID_CALLTIPS = wx.NewId() +ID_CALLTIPS_SHOW = wx.NewId() +ID_CALLTIPS_INSERT = wx.NewId() +ID_COPY_PLUS = wx.NewId() +ID_NAMESPACE = wx.NewId() +ID_PASTE_PLUS = wx.NewId() +ID_WRAP = wx.NewId() +ID_TOGGLE_MAXIMIZE = wx.NewId() +ID_SHOW_LINENUMBERS = wx.NewId() +ID_ENABLESHELLMODE = wx.NewId() +ID_ENABLEAUTOSYMPY = wx.NewId() +ID_AUTO_SAVESETTINGS = wx.NewId() +ID_SAVEACOPY = wx.NewId() +ID_SAVEHISTORY = wx.NewId() +ID_SAVEHISTORYNOW = wx.NewId() +ID_CLEARHISTORY = wx.NewId() +ID_SAVESETTINGS = wx.NewId() +ID_DELSETTINGSFILE = wx.NewId() +ID_EDITSTARTUPSCRIPT = wx.NewId() +ID_EXECSTARTUPSCRIPT = wx.NewId() +ID_SHOWPYSLICESTUTORIAL = wx.NewId() +ID_STARTUP = wx.NewId() +ID_SETTINGS = wx.NewId() +ID_FIND = wx.ID_FIND +ID_FINDNEXT = wx.NewId() +ID_FINDPREVIOUS = wx.NewId() +ID_SHOWTOOLS = wx.NewId() +ID_HIDEFOLDINGMARGIN = wx.NewId() + + + +class Frame(wx.Frame): + """Frame with standard menu items.""" + + revision = __revision__ + + def __init__(self, parent=None, id=-1, title='Editor', + pos=wx.DefaultPosition, size=wx.DefaultSize, + style=wx.DEFAULT_FRAME_STYLE,shellName='PyCrust'): + """Create a Frame instance.""" + wx.Frame.__init__(self, parent, id, title, pos, size, style) + self.CreateStatusBar() + self.SetStatusText('Frame') + self.shellName=shellName + import images + self.SetIcon(images.getPyIcon(shellName=shellName)) + self.__createMenus() + + self.iconized = False + self.findDlg = None + self.findData = wx.FindReplaceData() + self.findData.SetFlags(wx.FR_DOWN) + + self.Bind(wx.EVT_CLOSE, self.OnClose) + self.Bind(wx.EVT_ICONIZE, self.OnIconize) + + + def OnIconize(self, event): + """Event handler for Iconize.""" + self.iconized = event.Iconized() + + + def OnClose(self, event): + """Event handler for closing.""" + self.Destroy() + + + def __createMenus(self): + # File Menu + m = self.fileMenu = wx.Menu() + m.Append(ID_NEW, '&New \tCtrl+N', + 'New file') + m.Append(ID_OPEN, '&Open... \tCtrl+O', + 'Open file') + m.AppendSeparator() + m.Append(ID_REVERT, '&Revert \tCtrl+R', + 'Revert to last saved version') + m.Append(ID_CLOSE, '&Close \tCtrl+W', + 'Close file') + m.AppendSeparator() + m.Append(ID_SAVE, '&Save... \tCtrl+S', + 'Save file') + m.Append(ID_SAVEAS, 'Save &As \tCtrl+Shift+S', + 'Save file with new name') + if self.shellName in ['PySlices','SymPySlices']: + m.Append(ID_SAVEACOPY, 'Save A Cop&y', + 'Save a copy of the file without changing the current file') + m.AppendSeparator() + m.Append(ID_PRINT, '&Print... \tCtrl+P', + 'Print file') + m.AppendSeparator() + m.Append(ID_NAMESPACE, '&Update Namespace \tCtrl+Shift+N', + 'Update namespace for autocompletion and calltips') + m.AppendSeparator() + m.Append(ID_EXIT, 'E&xit\tCtrl+Q', 'Exit Program') + + # Edit + m = self.editMenu = wx.Menu() + m.Append(ID_UNDO, '&Undo \tCtrl+Z', + 'Undo the last action') + m.Append(ID_REDO, '&Redo \tCtrl+Y', + 'Redo the last undone action') + m.AppendSeparator() + m.Append(ID_CUT, 'Cu&t \tCtrl+X', + 'Cut the selection') + m.Append(ID_COPY, '&Copy \tCtrl+C', + 'Copy the selection') + m.Append(ID_COPY_PLUS, 'Cop&y Plus \tCtrl+Shift+C', + 'Copy the selection - retaining prompts') + m.Append(ID_PASTE, '&Paste \tCtrl+V', 'Paste from clipboard') + m.Append(ID_PASTE_PLUS, 'Past&e Plus \tCtrl+Shift+V', + 'Paste and run commands') + m.AppendSeparator() + m.Append(ID_CLEAR, 'Cle&ar', + 'Delete the selection') + m.Append(ID_SELECTALL, 'Select A&ll \tCtrl+A', + 'Select all text') + m.AppendSeparator() + m.Append(ID_EMPTYBUFFER, 'E&mpty Buffer...', + 'Delete all the contents of the edit buffer') + m.Append(ID_FIND, '&Find Text... \tCtrl+F', + 'Search for text in the edit buffer') + m.Append(ID_FINDNEXT, 'Find &Next \tCtrl+G', + 'Find next instance of the search text') + m.Append(ID_FINDPREVIOUS, 'Find Pre&vious \tCtrl+Shift+G', + 'Find previous instance of the search text') + + # View + m = self.viewMenu = wx.Menu() + m.Append(ID_WRAP, '&Wrap Lines\tCtrl+Shift+W', + 'Wrap lines at right edge', wx.ITEM_CHECK) + m.Append(ID_SHOW_LINENUMBERS, '&Show Line Numbers\tCtrl+Shift+L', + 'Show Line Numbers', wx.ITEM_CHECK) + m.Append(ID_TOGGLE_MAXIMIZE, '&Toggle Maximize\tF11', + 'Maximize/Restore Application') + if hasattr(self, 'ToggleTools'): + m.Append(ID_SHOWTOOLS, + 'Show &Tools\tF4', + 'Show the filling and other tools', wx.ITEM_CHECK) + if self.shellName==['PySlices','SymPySlices']: + m.Append(ID_HIDEFOLDINGMARGIN, + '&Hide Folding Margin', + 'Hide Folding Margin', wx.ITEM_CHECK) + + # Options + m = self.autocompMenu = wx.Menu() + m.Append(ID_AUTOCOMP_SHOW, 'Show &Auto Completion\tCtrl+Shift+A', + 'Show auto completion list', wx.ITEM_CHECK) + m.Append(ID_AUTOCOMP_MAGIC, 'Include &Magic Attributes\tCtrl+Shift+M', + 'Include attributes visible to __getattr__ and __setattr__', + wx.ITEM_CHECK) + m.Append(ID_AUTOCOMP_SINGLE, 'Include Single &Underscores\tCtrl+Shift+U', + 'Include attibutes prefixed by a single underscore', wx.ITEM_CHECK) + m.Append(ID_AUTOCOMP_DOUBLE, 'Include &Double Underscores\tCtrl+Shift+D', + 'Include attibutes prefixed by a double underscore', wx.ITEM_CHECK) + m = self.calltipsMenu = wx.Menu() + m.Append(ID_CALLTIPS_SHOW, 'Show Call &Tips\tCtrl+Shift+T', + 'Show call tips with argument signature and docstring', wx.ITEM_CHECK) + m.Append(ID_CALLTIPS_INSERT, '&Insert Call Tips\tCtrl+Shift+I', + '&Insert Call Tips', wx.ITEM_CHECK) + + m = self.optionsMenu = wx.Menu() + m.AppendMenu(ID_AUTOCOMP, '&Auto Completion', self.autocompMenu, + 'Auto Completion Options') + m.AppendMenu(ID_CALLTIPS, '&Call Tips', self.calltipsMenu, + 'Call Tip Options') + + m.AppendSeparator() + + self.historyMenu = wx.Menu() + self.historyMenu.Append(ID_SAVEHISTORY, '&Autosave History', + 'Automatically save history on close', wx.ITEM_CHECK) + self.historyMenu.Append(ID_SAVEHISTORYNOW, '&Save History Now', + 'Save history') + self.historyMenu.Append(ID_CLEARHISTORY, '&Clear History ', + 'Clear history') + m.AppendMenu(-1, "&History", self.historyMenu, "History Options") + + self.startupMenu = wx.Menu() + self.startupMenu.Append(ID_EXECSTARTUPSCRIPT, + 'E&xecute Startup Script', + 'Execute Startup Script', wx.ITEM_CHECK) + self.startupMenu.Append(ID_EDITSTARTUPSCRIPT, + '&Edit Startup Script...', + 'Edit Startup Script') + if self.shellName in ['PySlices','SymPySlices']: + self.startupMenu.Append(ID_SHOWPYSLICESTUTORIAL, + '&Show PySlices Tutorial', + 'Show PySlices Tutorial', wx.ITEM_CHECK) + m.AppendMenu(ID_STARTUP, '&Startup', self.startupMenu, 'Startup Options') + + self.settingsMenu = wx.Menu() + if self.shellName in ['PySlices','SymPySlices']: + self.settingsMenu.Append(ID_ENABLESHELLMODE, + '&Enable Shell Mode', + 'Enable Shell Mode', wx.ITEM_CHECK) + if self.shellName == 'SymPySlices': + self.settingsMenu.Append(ID_ENABLEAUTOSYMPY, + '&Enable "Auto-Sympy" Conversions for Undefined Variables', + 'Enable "Auto-Sympy" Conversions', wx.ITEM_CHECK) + self.settingsMenu.Append(ID_AUTO_SAVESETTINGS, + '&Auto Save Settings', + 'Automatically save settings on close', wx.ITEM_CHECK) + self.settingsMenu.Append(ID_SAVESETTINGS, + '&Save Settings', + 'Save settings now') + self.settingsMenu.Append(ID_DELSETTINGSFILE, + '&Revert to default', + 'Revert to the default settings') + m.AppendMenu(ID_SETTINGS, '&Settings', self.settingsMenu, 'Settings Options') + + m = self.helpMenu = wx.Menu() + m.Append(ID_HELP, '&Help\tF1', 'Help!') + m.AppendSeparator() + m.Append(ID_ABOUT, '&About...', 'About this program') + + b = self.menuBar = wx.MenuBar() + b.Append(self.fileMenu, '&File') + b.Append(self.editMenu, '&Edit') + b.Append(self.viewMenu, '&View') + b.Append(self.optionsMenu, '&Options') + b.Append(self.helpMenu, '&Help') + self.SetMenuBar(b) + + self.Bind(wx.EVT_MENU, self.OnFileNew, id=ID_NEW) + self.Bind(wx.EVT_MENU, self.OnFileOpen, id=ID_OPEN) + self.Bind(wx.EVT_MENU, self.OnFileRevert, id=ID_REVERT) + self.Bind(wx.EVT_MENU, self.OnFileClose, id=ID_CLOSE) + self.Bind(wx.EVT_MENU, self.OnFileSave, id=ID_SAVE) + self.Bind(wx.EVT_MENU, self.OnFileSaveAs, id=ID_SAVEAS) + self.Bind(wx.EVT_MENU, self.OnFileSaveACopy, id=ID_SAVEACOPY) + self.Bind(wx.EVT_MENU, self.OnFileUpdateNamespace, id=ID_NAMESPACE) + self.Bind(wx.EVT_MENU, self.OnFilePrint, id=ID_PRINT) + self.Bind(wx.EVT_MENU, self.OnExit, id=ID_EXIT) + self.Bind(wx.EVT_MENU, self.OnUndo, id=ID_UNDO) + self.Bind(wx.EVT_MENU, self.OnRedo, id=ID_REDO) + self.Bind(wx.EVT_MENU, self.OnCut, id=ID_CUT) + self.Bind(wx.EVT_MENU, self.OnCopy, id=ID_COPY) + self.Bind(wx.EVT_MENU, self.OnCopyPlus, id=ID_COPY_PLUS) + self.Bind(wx.EVT_MENU, self.OnPaste, id=ID_PASTE) + self.Bind(wx.EVT_MENU, self.OnPastePlus, id=ID_PASTE_PLUS) + self.Bind(wx.EVT_MENU, self.OnClear, id=ID_CLEAR) + self.Bind(wx.EVT_MENU, self.OnSelectAll, id=ID_SELECTALL) + self.Bind(wx.EVT_MENU, self.OnEmptyBuffer, id=ID_EMPTYBUFFER) + self.Bind(wx.EVT_MENU, self.OnAbout, id=ID_ABOUT) + self.Bind(wx.EVT_MENU, self.OnHelp, id=ID_HELP) + self.Bind(wx.EVT_MENU, self.OnAutoCompleteShow, id=ID_AUTOCOMP_SHOW) + self.Bind(wx.EVT_MENU, self.OnAutoCompleteMagic, id=ID_AUTOCOMP_MAGIC) + self.Bind(wx.EVT_MENU, self.OnAutoCompleteSingle, id=ID_AUTOCOMP_SINGLE) + self.Bind(wx.EVT_MENU, self.OnAutoCompleteDouble, id=ID_AUTOCOMP_DOUBLE) + self.Bind(wx.EVT_MENU, self.OnCallTipsShow, id=ID_CALLTIPS_SHOW) + self.Bind(wx.EVT_MENU, self.OnCallTipsInsert, id=ID_CALLTIPS_INSERT) + self.Bind(wx.EVT_MENU, self.OnWrap, id=ID_WRAP) + self.Bind(wx.EVT_MENU, self.OnToggleMaximize, id=ID_TOGGLE_MAXIMIZE) + self.Bind(wx.EVT_MENU, self.OnShowLineNumbers, id=ID_SHOW_LINENUMBERS) + self.Bind(wx.EVT_MENU, self.OnEnableShellMode, id=ID_ENABLESHELLMODE) + self.Bind(wx.EVT_MENU, self.OnEnableAutoSympy, id=ID_ENABLEAUTOSYMPY) + self.Bind(wx.EVT_MENU, self.OnAutoSaveSettings, id=ID_AUTO_SAVESETTINGS) + self.Bind(wx.EVT_MENU, self.OnSaveHistory, id=ID_SAVEHISTORY) + self.Bind(wx.EVT_MENU, self.OnSaveHistoryNow, id=ID_SAVEHISTORYNOW) + self.Bind(wx.EVT_MENU, self.OnClearHistory, id=ID_CLEARHISTORY) + self.Bind(wx.EVT_MENU, self.OnSaveSettings, id=ID_SAVESETTINGS) + self.Bind(wx.EVT_MENU, self.OnDelSettingsFile, id=ID_DELSETTINGSFILE) + self.Bind(wx.EVT_MENU, self.OnEditStartupScript, id=ID_EDITSTARTUPSCRIPT) + self.Bind(wx.EVT_MENU, self.OnExecStartupScript, id=ID_EXECSTARTUPSCRIPT) + self.Bind(wx.EVT_MENU, self.OnShowPySlicesTutorial, id=ID_SHOWPYSLICESTUTORIAL) + self.Bind(wx.EVT_MENU, self.OnFindText, id=ID_FIND) + self.Bind(wx.EVT_MENU, self.OnFindNext, id=ID_FINDNEXT) + self.Bind(wx.EVT_MENU, self.OnFindPrevious, id=ID_FINDPREVIOUS) + self.Bind(wx.EVT_MENU, self.OnToggleTools, id=ID_SHOWTOOLS) + self.Bind(wx.EVT_MENU, self.OnHideFoldingMargin, id=ID_HIDEFOLDINGMARGIN) + + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_NEW) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_OPEN) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_REVERT) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_CLOSE) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_SAVE) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_SAVEAS) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_NAMESPACE) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_PRINT) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_UNDO) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_REDO) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_CUT) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_COPY) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_COPY_PLUS) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_PASTE) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_PASTE_PLUS) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_CLEAR) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_SELECTALL) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_EMPTYBUFFER) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_AUTOCOMP_SHOW) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_AUTOCOMP_MAGIC) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_AUTOCOMP_SINGLE) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_AUTOCOMP_DOUBLE) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_CALLTIPS_SHOW) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_CALLTIPS_INSERT) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_WRAP) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_SHOW_LINENUMBERS) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_ENABLESHELLMODE) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_ENABLEAUTOSYMPY) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_AUTO_SAVESETTINGS) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_SAVESETTINGS) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_DELSETTINGSFILE) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_EXECSTARTUPSCRIPT) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_SHOWPYSLICESTUTORIAL) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_SAVEHISTORY) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_SAVEHISTORYNOW) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_CLEARHISTORY) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_EDITSTARTUPSCRIPT) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_FIND) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_FINDNEXT) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_FINDPREVIOUS) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_SHOWTOOLS) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_HIDEFOLDINGMARGIN) + + self.Bind(wx.EVT_ACTIVATE, self.OnActivate) + self.Bind(wx.EVT_FIND, self.OnFindNext) + self.Bind(wx.EVT_FIND_NEXT, self.OnFindNext) + self.Bind(wx.EVT_FIND_CLOSE, self.OnFindClose) + + + + def OnShowLineNumbers(self, event): + win = wx.Window.FindFocus() + if hasattr(win, 'lineNumbers'): + win.lineNumbers = event.IsChecked() + win.setDisplayLineNumbers(win.lineNumbers) + + def OnToggleMaximize(self, event): + self.Maximize(not self.IsMaximized()) + + def OnFileNew(self, event): + self.bufferNew() + + def OnFileOpen(self, event): + self.bufferOpen() + + def OnFileRevert(self, event): + self.bufferRevert() + + def OnFileClose(self, event): + self.bufferClose() + + def OnFileSave(self, event): + self.bufferSave() + + def OnFileSaveAs(self, event): + self.bufferSaveAs() + + def OnFileSaveACopy(self, event): + self.bufferSaveACopy() + + def OnFileUpdateNamespace(self, event): + self.updateNamespace() + + def OnFilePrint(self, event): + self.bufferPrint() + + def OnExit(self, event): + self.Close(False) + + def OnUndo(self, event): + win = wx.Window.FindFocus() + win.Undo() + + def OnRedo(self, event): + win = wx.Window.FindFocus() + win.Redo() + + def OnCut(self, event): + win = wx.Window.FindFocus() + win.Cut() + + def OnCopy(self, event): + win = wx.Window.FindFocus() + win.Copy() + + def OnCopyPlus(self, event): + win = wx.Window.FindFocus() + win.CopyWithPrompts() + + def OnPaste(self, event): + win = wx.Window.FindFocus() + win.Paste() + + def OnPastePlus(self, event): + win = wx.Window.FindFocus() + win.PasteAndRun() + + def OnClear(self, event): + win = wx.Window.FindFocus() + win.Clear() + + def OnEmptyBuffer(self, event): + win = wx.Window.FindFocus() + d = wx.MessageDialog(self, + "Are you sure you want to clear the edit buffer,\n" + "deleting all the text?", + "Empty Buffer", wx.OK | wx.CANCEL | wx.ICON_QUESTION) + answer = d.ShowModal() + d.Destroy() + if (answer == wx.ID_OK): + win.ClearAll() + if hasattr(win,'prompt'): + win.prompt() + + def OnSelectAll(self, event): + win = wx.Window.FindFocus() + win.SelectAll() + + def OnAbout(self, event): + """Display an About window.""" + title = 'About' + text = 'Your message here.' + dialog = wx.MessageDialog(self, text, title, + wx.OK | wx.ICON_INFORMATION) + dialog.ShowModal() + dialog.Destroy() + + def OnHelp(self, event): + """Display a Help window.""" + title = 'Help' + text = "Type 'shell.help()' in the shell window." + dialog = wx.MessageDialog(self, text, title, + wx.OK | wx.ICON_INFORMATION) + dialog.ShowModal() + dialog.Destroy() + + def OnAutoCompleteShow(self, event): + win = wx.Window.FindFocus() + win.autoComplete = event.IsChecked() + + def OnAutoCompleteMagic(self, event): + win = wx.Window.FindFocus() + win.autoCompleteIncludeMagic = event.IsChecked() + + def OnAutoCompleteSingle(self, event): + win = wx.Window.FindFocus() + win.autoCompleteIncludeSingle = event.IsChecked() + + def OnAutoCompleteDouble(self, event): + win = wx.Window.FindFocus() + win.autoCompleteIncludeDouble = event.IsChecked() + + def OnCallTipsShow(self, event): + win = wx.Window.FindFocus() + win.autoCallTip = event.IsChecked() + + def OnCallTipsInsert(self, event): + win = wx.Window.FindFocus() + win.callTipInsert = event.IsChecked() + + def OnWrap(self, event): + win = wx.Window.FindFocus() + win.SetWrapMode(event.IsChecked()) + wx.FutureCall(1, self.shell.EnsureCaretVisible) + + def OnSaveHistory(self, event): + self.autoSaveHistory = event.IsChecked() + + def OnSaveHistoryNow(self, event): + self.SaveHistory() + + def OnClearHistory(self, event): + self.shell.clearHistory() + + def OnEnableShellMode(self, event): + self.enableShellMode = event.IsChecked() + + def OnEnableAutoSympy(self, event): + self.enableAutoSympy = event.IsChecked() + + def OnHideFoldingMargin(self, event): + self.hideFoldingMargin = event.IsChecked() + + def OnAutoSaveSettings(self, event): + self.autoSaveSettings = event.IsChecked() + + def OnSaveSettings(self, event): + self.DoSaveSettings() + + def OnDelSettingsFile(self, event): + if self.config is not None: + d = wx.MessageDialog( + self, "Do you want to revert to the default settings?\n" + + "A restart is needed for the change to take effect", + "Warning", wx.OK | wx.CANCEL | wx.ICON_QUESTION) + answer = d.ShowModal() + d.Destroy() + if (answer == wx.ID_OK): + self.config.DeleteAll() + self.LoadSettings() + + + def OnEditStartupScript(self, event): + if hasattr(self, 'EditStartupScript'): + self.EditStartupScript() + + def OnExecStartupScript(self, event): + self.execStartupScript = event.IsChecked() + self.SaveSettings(force=True) + + def OnShowPySlicesTutorial(self,event): + self.showPySlicesTutorial = event.IsChecked() + self.SaveSettings(force=True) + + def OnFindText(self, event): + if self.findDlg is not None: + return + win = wx.Window.FindFocus() + if self.shellName == 'PyCrust': + self.findDlg = wx.FindReplaceDialog(win, self.findData, + "Find",wx.FR_NOWHOLEWORD) + else: + self.findDlg = wx.FindReplaceDialog(win, self.findData, + "Find & Replace", wx.FR_NOWHOLEWORD|wx.FR_REPLACEDIALOG) + self.findDlg.Show() + + def OnFindNext(self, event,backward=False): + if backward and (self.findData.GetFlags() & wx.FR_DOWN): + self.findData.SetFlags( self.findData.GetFlags() ^ wx.FR_DOWN ) + elif not backward and not (self.findData.GetFlags() & wx.FR_DOWN): + self.findData.SetFlags( self.findData.GetFlags() ^ wx.FR_DOWN ) + + if not self.findData.GetFindString(): + self.OnFindText(event) + return + if isinstance(event, wx.FindDialogEvent): + win = self.findDlg.GetParent() + else: + win = wx.Window.FindFocus() + win.DoFindNext(self.findData, self.findDlg) + if self.findDlg is not None: + self.OnFindClose(None) + + def OnFindPrevious(self, event): + self.OnFindNext(event,backward=True) + + def OnFindClose(self, event): + self.findDlg.Destroy() + self.findDlg = None + + def OnToggleTools(self, event): + self.ToggleTools() + + + def OnUpdateMenu(self, event): + """Update menu items based on current status and context.""" + win = wx.Window.FindFocus() + id = event.GetId() + event.Enable(True) + try: + if id == ID_NEW: + event.Enable(hasattr(self, 'bufferNew')) + elif id == ID_OPEN: + event.Enable(hasattr(self, 'bufferOpen')) + elif id == ID_REVERT: + event.Enable(hasattr(self, 'bufferRevert') + and self.hasBuffer()) + elif id == ID_CLOSE: + event.Enable(hasattr(self, 'bufferClose') + and self.hasBuffer()) + elif id == ID_SAVE: + event.Enable(hasattr(self, 'bufferSave') + and self.bufferHasChanged()) + elif id == ID_SAVEAS: + event.Enable(hasattr(self, 'bufferSaveAs') + and self.hasBuffer()) + elif id == ID_SAVEACOPY: + event.Enable(hasattr(self, 'bufferSaveACopy') + and self.hasBuffer()) + elif id == ID_NAMESPACE: + event.Enable(hasattr(self, 'updateNamespace') + and self.hasBuffer()) + elif id == ID_PRINT: + event.Enable(hasattr(self, 'bufferPrint') + and self.hasBuffer()) + elif id == ID_UNDO: + event.Enable(win.CanUndo()) + elif id == ID_REDO: + event.Enable(win.CanRedo()) + elif id == ID_CUT: + event.Enable(win.CanCut()) + elif id == ID_COPY: + event.Enable(win.CanCopy()) + elif id == ID_COPY_PLUS: + event.Enable(win.CanCopy() and hasattr(win, 'CopyWithPrompts')) + elif id == ID_PASTE: + event.Enable(win.CanPaste()) + elif id == ID_PASTE_PLUS: + event.Enable(win.CanPaste() and hasattr(win, 'PasteAndRun')) + elif id == ID_CLEAR: + event.Enable(win.CanCut()) + elif id == ID_SELECTALL: + event.Enable(hasattr(win, 'SelectAll')) + elif id == ID_EMPTYBUFFER: + event.Enable(hasattr(win, 'ClearAll') and not win.GetReadOnly()) + elif id == ID_AUTOCOMP_SHOW: + event.Check(win.autoComplete) + elif id == ID_AUTOCOMP_MAGIC: + event.Check(win.autoCompleteIncludeMagic) + elif id == ID_AUTOCOMP_SINGLE: + event.Check(win.autoCompleteIncludeSingle) + elif id == ID_AUTOCOMP_DOUBLE: + event.Check(win.autoCompleteIncludeDouble) + elif id == ID_CALLTIPS_SHOW: + event.Check(win.autoCallTip) + elif id == ID_CALLTIPS_INSERT: + event.Check(win.callTipInsert) + elif id == ID_WRAP: + event.Check(win.GetWrapMode()) + + elif id == ID_SHOW_LINENUMBERS: + event.Check(win.lineNumbers) + elif id == ID_ENABLESHELLMODE: + event.Check(self.enableShellMode) + event.Enable(self.config is not None) + elif id == ID_ENABLEAUTOSYMPY: + event.Check(self.enableAutoSympy) + event.Enable(self.config is not None) + elif id == ID_AUTO_SAVESETTINGS: + event.Check(self.autoSaveSettings) + event.Enable(self.config is not None) + elif id == ID_SAVESETTINGS: + event.Enable(self.config is not None and + hasattr(self, 'DoSaveSettings')) + elif id == ID_DELSETTINGSFILE: + event.Enable(self.config is not None) + + elif id == ID_EXECSTARTUPSCRIPT: + event.Check(self.execStartupScript) + event.Enable(self.config is not None) + + elif id == ID_SHOWPYSLICESTUTORIAL: + event.Check(self.showPySlicesTutorial) + event.Enable(self.config is not None) + + elif id == ID_SAVEHISTORY: + event.Check(self.autoSaveHistory) + event.Enable(self.dataDir is not None) + elif id == ID_SAVEHISTORYNOW: + event.Enable(self.dataDir is not None and + hasattr(self, 'SaveHistory')) + elif id == ID_CLEARHISTORY: + event.Enable(self.dataDir is not None) + + elif id == ID_EDITSTARTUPSCRIPT: + event.Enable(hasattr(self, 'EditStartupScript')) + event.Enable(self.dataDir is not None) + + elif id == ID_FIND: + event.Enable(hasattr(win, 'DoFindNext')) + elif id == ID_FINDNEXT: + event.Enable(hasattr(win, 'DoFindNext')) + elif id == ID_FINDPREVIOUS: + event.Enable(hasattr(win, 'DoFindNext')) + + elif id == ID_SHOWTOOLS: + event.Check(self.ToolsShown()) + + elif id == ID_HIDEFOLDINGMARGIN: + event.Check(self.hideFoldingMargin) + event.Enable(self.config is not None) + + else: + event.Enable(False) + except AttributeError: + # This menu option is not supported in the current context. + event.Enable(False) + + + def OnActivate(self, event): + """ + Event Handler for losing the focus of the Frame. Should close + Autocomplete listbox, if shown. + """ + if not event.GetActive(): + # If autocomplete active, cancel it. Otherwise, the + # autocomplete list will stay visible on top of the + # z-order after switching to another application + win = wx.Window.FindFocus() + if hasattr(win, 'AutoCompActive') and win.AutoCompActive(): + win.AutoCompCancel() + event.Skip() + + + + def LoadSettings(self, config): + """Called by derived classes to load settings specific to the Frame""" + pos = wx.Point(config.ReadInt('Window/PosX', -1), + config.ReadInt('Window/PosY', -1)) + + size = wx.Size(config.ReadInt('Window/Width', -1), + config.ReadInt('Window/Height', -1)) + + self.SetSize(size) + self.Move(pos) + + + def SaveSettings(self, config): + """Called by derived classes to save Frame settings to a wx.Config object""" + + # TODO: track position/size so we can save it even if the + # frame is maximized or iconized. + if not self.iconized and not self.IsMaximized(): + w, h = self.GetSize() + config.WriteInt('Window/Width', w) + config.WriteInt('Window/Height', h) + + px, py = self.GetPosition() + config.WriteInt('Window/PosX', px) + config.WriteInt('Window/PosY', py) + + + + +class ShellFrameMixin: + """ + A mix-in class for frames that will have a Shell or a Crust window + and that want to add history, startupScript and other common + functionality. + """ + def __init__(self, config, dataDir): + self.config = config + self.dataDir = dataDir + self.startupScript = os.environ.get('PYTHONSTARTUP') + if not self.startupScript and self.dataDir: + self.startupScript = os.path.join(self.dataDir, 'startup') + + self.autoSaveSettings = False + self.autoSaveHistory = False + + # We need this one before we have a chance to load the settings... + self.execStartupScript = True + self.showPySlicesTutorial = True + self.enableShellMode = False + self.enableAutoSympy = True + self.hideFoldingMargin = False + if self.config: + self.execStartupScript = \ + self.config.ReadBool('Options/ExecStartupScript', True) + self.showPySlicesTutorial = \ + self.config.ReadBool('Options/ShowPySlicesTutorial', True) + self.enableShellMode = \ + self.config.ReadBool('Options/EnableShellMode', False) + self.enableAutoSympy = \ + self.config.ReadBool('Options/EnableAutoSympy', True) + self.hideFoldingMargin = \ + self.config.ReadBool('Options/HideFoldingMargin', True) + + def OnHelp(self, event): + """Display a Help window.""" + import wx.lib.dialogs + title = 'Help on key bindings' + + text = wx.py.shell.HELP_TEXT + + dlg = wx.lib.dialogs.ScrolledMessageDialog(self, text, title, + size = ((700, 540))) + fnt = wx.Font(10, wx.TELETYPE, wx.NORMAL, wx.NORMAL) + dlg.GetChildren()[0].SetFont(fnt) + dlg.GetChildren()[0].SetInsertionPoint(0) + dlg.ShowModal() + dlg.Destroy() + + + def LoadSettings(self): + if self.config is not None: + self.autoSaveSettings = \ + self.config.ReadBool('Options/AutoSaveSettings', False) + self.execStartupScript = \ + self.config.ReadBool('Options/ExecStartupScript', True) + self.autoSaveHistory = \ + self.config.ReadBool('Options/AutoSaveHistory', False) + + self.showPySlicesTutorial = \ + self.config.ReadBool('Options/ShowPySlicesTutorial', True) + self.enableShellMode = \ + self.config.ReadBool('Options/EnableShellMode', False) + self.enableAutoSympy = \ + self.config.ReadBool('Options/EnableAutoSympy', True) + self.hideFoldingMargin = \ + self.config.ReadBool('Options/HideFoldingMargin', True) + + self.LoadHistory() + + + def SaveSettings(self, force=False): + if self.config is not None: + # always save these + self.config.WriteBool('Options/AutoSaveSettings', + self.autoSaveSettings) + + if self.autoSaveSettings or force: + self.config.WriteBool('Options/AutoSaveHistory', + self.autoSaveHistory) + self.config.WriteBool('Options/ExecStartupScript', + self.execStartupScript) + self.config.WriteBool('Options/ShowPySlicesTutorial', + self.showPySlicesTutorial) + self.config.WriteBool('Options/EnableShellMode', + self.enableShellMode) + self.config.WriteBool('Options/EnableAutoSympy', + self.enableAutoSympy) + self.config.WriteBool('Options/HideFoldingMargin', + self.hideFoldingMargin) + if self.autoSaveHistory: + self.SaveHistory() + + + + def SaveHistory(self): + if self.dataDir: + try: + name = os.path.join(self.dataDir, 'history') + f = file(name, 'w') + hist = [] + enc = wx.GetDefaultPyEncoding() + for h in self.shell.history: + if isinstance(h, unicode): + h = h.encode(enc) + hist.append(h) + hist = '\x00\n'.join(hist) + f.write(hist) + f.close() + except: + d = wx.MessageDialog(self, "Error saving history file.", + "Error", wx.ICON_EXCLAMATION|wx.OK) + d.ShowModal() + d.Destroy() + raise + + def LoadHistory(self): + if self.dataDir: + name = os.path.join(self.dataDir, 'history') + if os.path.exists(name): + try: + f = file(name, 'U') + hist = f.read() + f.close() + self.shell.history = hist.split('\x00\n') + dispatcher.send(signal="Shell.loadHistory", + history=self.shell.history) + except: + d = wx.MessageDialog(self, "Error loading history file.", + "Error", wx.ICON_EXCLAMATION|wx.OK) + d.ShowModal() + d.Destroy() + + + def bufferHasChanged(self): + # the shell buffers can always be saved + return True + + def bufferSave(self): + import time + appname = wx.GetApp().GetAppName() + default = appname + '-' + time.strftime("%Y%m%d-%H%M.py") + fileName = wx.FileSelector("Save File As", "Saving", + default_filename=default, + default_extension="py", + wildcard="*.py", + flags = wx.SAVE | wx.OVERWRITE_PROMPT) + if not fileName: + return + + text = self.shell.GetText() + +## This isn't working currently... +## d = wx.MessageDialog(self,u'Save source code only?\n' + \ +## 'Answering yes will only save lines starting with >>> and ...', +## u'Question', wx.YES_NO | wx.ICON_QUESTION) +## yes_no = d.ShowModal() +## if yes_no == wx.ID_YES: +## m = re.findall('^[>\.]{3,3} (.*)\r', text, re.MULTILINE | re.LOCALE) +## text = '\n'.join(m) +## d.Destroy() + + try: + f = open(fileName, "w") + f.write(text) + f.close() + except: + d = wx.MessageDialog(self, u'Error saving session',u'Error', + wx.OK | wx.ICON_ERROR) + d.ShowModal() + d.Destroy() + + + def EditStartupScript(self): + if os.path.exists(self.startupScript): + text = file(self.startupScript, 'U').read() + else: + text = '' + + dlg = EditStartupScriptDialog(self, self.startupScript, text) + if dlg.ShowModal() == wx.ID_OK: + text = dlg.GetText() + try: + f = file(self.startupScript, 'w') + f.write(text) + f.close() + except: + d = wx.MessageDialog(self, "Error saving startup file.", + "Error", wx.ICON_EXCLAMATION|wx.OK) + d.ShowModal() + d.Destroy() + + + +class EditStartupScriptDialog(wx.Dialog): + def __init__(self, parent, fileName, text): + wx.Dialog.__init__(self, parent, size=(425,350), + title="Edit Startup Script", + style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) + + pst = wx.StaticText(self, -1, "Path:") + ptx = wx.TextCtrl(self, -1, fileName, style=wx.TE_READONLY) + self.editor = editwindow.EditWindow(self) + self.editor.SetText(text) + wx.CallAfter(self.editor.SetFocus) + + ok = wx.Button(self, wx.ID_OK) + cancel = wx.Button(self, wx.ID_CANCEL) + + mainSizer = wx.BoxSizer(wx.VERTICAL) + + pthSizer = wx.BoxSizer(wx.HORIZONTAL) + pthSizer.Add(pst, flag=wx.ALIGN_CENTER_VERTICAL) + pthSizer.Add((5,5)) + pthSizer.Add(ptx, 1) + mainSizer.Add(pthSizer, 0, wx.EXPAND|wx.ALL, 10) + + mainSizer.Add(self.editor, 1, wx.EXPAND|wx.LEFT|wx.RIGHT, 10) + + btnSizer = wx.BoxSizer(wx.HORIZONTAL) + btnSizer.Add((5,5), 1) + btnSizer.Add(ok) + btnSizer.Add((5,5), 1) + btnSizer.Add(cancel) + btnSizer.Add((5,5), 1) + mainSizer.Add(btnSizer, 0, wx.EXPAND|wx.ALL, 10) + + self.SetSizer(mainSizer) + self.Layout() + + + def GetText(self): + return self.editor.GetText() diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/images.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/images.py new file mode 100644 index 0000000..f3cb5a1 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/images.py @@ -0,0 +1,215 @@ +"""Support for icons.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com> / David Mashburn <david.n.mashburn@gmail.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import wx +import cStringIO + + +def getPyIcon(shellName='PyCrust'): + icon = wx.EmptyIcon() + icon.CopyFromBitmap(getPyBitmap(shellName)) + return icon + +def getPyBitmap(shellName='PyCrust'): + return wx.BitmapFromImage(getPyImage(shellName)) + +def getPyImage(shellName='PyCrust'): + stream = cStringIO.StringIO(getPyData(shellName)) + return wx.ImageFromStream(stream) + +def getPyData(shellName='PyCrust'): + if shellName=='PyCrust': + return \ +'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\ +\x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x04\ +\x95IDATx\x9c\xed\x97?lSG\x1c\xc7?\x97\x98\xd8Q\xa3\xdeY\xa2j\x06\xa4\xf7"QJ\ +\xbb<3@\x01\xa9\xc2\x0c\xa8!\x1d\x1c6\xcaB\xa8D[uI2\xf4\x8f\xe8\x103\xb4\xa2\ +,5\x0b\x03\x032C\xab\xc0\x92dh:t\xc0)*E\xcd@<Q\x01Rl\t\xd4D\xaa\xe4{R\xd0{&\ +\xa5\xd7\xe1\xfc\xec\xe7\xfciR\x08e\xe9O\xb2\xee|\xfe\xbd\xfb}~\xdf\xdf\xbd\ +\xbb\xb3PJ\xf1"\xad\xe3\x85F\xff\x1f\xe0y\x03h\xad\xcdA\xc7~\xb4\xd6f-\x9f\ +\xc4\xf3\x0c>Y\x1c#\x97\xddCUk\xf4B\x8d3\x9f\x8d\x9a\x9bU%\xe2~b\xab\xdf\x82\ +\x83N+\xd3\xe92\\\x1f\xcf\x93\xdd\x9f\xa1\xaa5\x95\xf9\n\xe7\xf3y\xe2\x10[V\ +\x82H\xe6\xd3G\x1dN\xf7\xc3\xa7\xc7a\xc0\x83\xc3\xc7\xf3\xcc\xcc\xcd\xe3(\ +\x85\xdb\xe7\xf2\xc9\xe8X\x1b\xe43+\x10\xd5\xb6\x94\x87Z\xe8\x90NU\x91I@\x00\ +\x06\xbe\x18\xb7J\x98\xca$`\x98\xb9]&{,\x8fRV\x85\xa7V@k\x9bq)o\x83+\t\xe9T\ +\xd5f\x95\x02\x91\xb4~_\r\xd9\xb6\xbaP\x03\x04n\x9f\xcbDa\xb8\t\xfe\xaf\x17a\ +<\xe3\xc8\x94lo\x9b\xd6\xa8\xf4\x80\x07\xb7o\xcd\xe0\x0c\x0e\xa2R\x8a\xb4\ +\x93n\xbal\x1a`e\xe0U\xc1\xd6\xb0\xb8\n\x99\x91"\x93\xaf\xba\xe4\x0ed\xda|6,\ +\x81\xd6\xda\x9c|\xab]\xea\xcd\x04\x8f\x9b\t\xad\nz\xa1\x02\x80\xdb\xe7R\x1a\ +\xcf\xa3\xb56\xeb\x02D5\x9e\xf8\xdc\xe1T\xff\xd3\x05\x8e\x82\x83U\xe1Z\xb1\ +\x18\x9b\xbf\x06\xacQ\x82H\xea\x01/Z@Ut\x08R\xb4$}\x16\xd3\x81A&%\xde\xee\ +\xbev\x80x\xe0]{\xb2\x1cR\xa5\xe6C*\xb5\xf1\xc4Q\xa6"e\xfbQ\x1b\x8dE\xe6\x87\ +>\xaa[Q\xadi\x0b\xb0r\x8f\x9e.\xc3t\xb9\xc4]\xaf5\xf6\xfe\xdb\xddt&\x02\xfa\ +\x9c\xf5\x01\xe2A\xa2\xbeX\x01>]ntR\x12\xe3[\x00\x01\x98\x89\x11[_\xed\xafn\ +\xab\x81U\xa0\xe7I7\x00\x97o\x04\xcd\x89\x06<;\xe9\x80\x07]i\x97\xc17\x1f\ +\xd2\xd3\x91`\xe9\xaf?\x01p^Y\x06Z\n\xfau8?a\xfb]i\x97\xec\xa1\x8c\x05(|\xd8\ +N\xba\xb3\xab\x87\xfb\x8f\x97\xd8\xd9\xd5\x03\xc0\xfd\xc7K\xec\xd8\xd6\xdd\ +\xfc\xfd\xc1r\xd0\xf4\x01\xda~\x03H\xf4\x04\xd4j :\xb75\xc7\xae\xfd\xbcLW\ +\xda\xa5\xf0M\x1e\t\xcc\xcc\xcdq\xa9P@\x8c\xf5fL\xdaHF\x16g\x9a\x19\xad\xcc\ +\xee\xcb\xa3\n\xad\xa1\xda\xf1\x08\xef\xe5\x97x\xf8\xc8f\xf8\xc7\x93:\xdb;\ +\x93M\xc8\x08j\xc7\xb6n\x1e,\x07m`\x97o\x04|;>\xd1T\xc4\x17\x8a\x13\xb9\xc3\ +\x88\x01\x0fs\xa4\x9cc}\xf3A\x190\x82\x1f\xddR{-\x1bV\xfc\xd8f\xba\xbd3\xd9\ +\x06\x15\x07\xbb\xf8\xd3\x12\xdf]-"\x93\xb2\xb1C*\xde\xcd\x1d\xde\xccN(\xc1\ +\xae\x17"\xd0#+<j\x17m{\xcd\x9bj\x00.\xaf\xf0Xb\xb8\xdfA\xa6\x14\x18\x03\x06\ +\xb4o\xcf\x8d\xc4\xbervc\x86M\xdaz\x80\x00\x95T\x19?\xd0 @&%~c\xbc\xe3W\xaf\ +\xb4e\x00\xffh\xc6@\xbd\x11\xbc\xde\x1a\xfe\xef.\xa5\xa2q4\n0\x81\xad\xe9\ +\xae7<\x12\xaf\xf5\xc2hy\xaa\xe97\x9cS\\\x98\xb2\x0e\x03\xb1\xcdhW\xdaC\x1a\ +\xa0\xa2\xa0\x0e"\x14`\xb0Y\x85\x1b\x1f\x12\xaa7\x03)\xd9\x84\xa8\xccW\xb8{\ +\xa7L\xe2\xde\x02\x94\xc6Gp_\xcf\x80\x90\x98\xd0g\xf4\xac\x82Pc\x82\x1a\xd5\ +\x10\x08}&\xa7J\xcc\xde.1]m\x80\xf6+\xee\xfd\xae\x9bo\xc4\xf0;\x80\xef\x90\ +\x0e\x04\x06`Q!\x02\x05\xc2 \xb5\xc2\x95\x15d\xb4C&[\xf7\xd2\x04\x80\xbb\xdb\ +\x9e\xd1\x8e\x02\x90\xd8\xd4$ I\x87\x80\xf1\xf1\xdc!4\xc3\x88\x94}\xd8,TH\ +\xbb.5m\xf0C\x9f3\x1f\r\x01\x96.\x82\x1a9\xe9Q\xb8\xd2\xf8\xf25\x0c\xbe\xe7#\ +\x92\x12\x1d[\x03\t\x00E\xf4\xa6\t\xaaZ7`$\x18\x90\xf8\xf8\x80JK\x94\xa1\x01\ +\x07\xb8\x0e~X\xc3\xed\x16\xf8)\xf8~j\x12B\rI\x89_\xf7!0 \x04\xf9Q\xc0\x18\ +\x0c\xd1i\xea\x13\xb7\x04\xc0\x89\x93C\xabj\xb6\xf7@\x96\xd9_J|0:\x86R\n\xb7\ +\xd7@\xaa%\x9d\xa3$\xba.\x90RA]\xe3\x87\x1a\x89\xdd\xefeR\xc2\x1a\'\xa8\x1f\ +\x82\x0e-@m\xd1\xde\x076\xbc\x15\x97~(\x9a\x89b\x9e\xd9[s\xab!\xf7g\xd6\x1c\ +\x8f\xdb\xbel\x8e\xa1S\xc7\xda\xc6\xe6\xee\xccs\xe9\xdcYnV\x95\xd8\xf2?&q+\ +\x9c\x1b1\xf3\xbf\xcd3{\xfdJ\xdb\xf8\xde\xfd\x19.\\\xad\x08\x80\xbf\x01\xd1\ +\x86\xfa\x8b\xc7\xc0\xc8\xb7\x00\x00\x00\x00IEND\xaeB`\x82' + elif shellName=='PySlices': + return \ +'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\ +\x00szz\xf4\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x06bKGD\x00\xff\ +\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00\x00\x0b\ +\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd8\n\x16\x03#\x0eV,\xc0?\x00\ +\x00\x06\xb8IDATX\xc3\xbd\x97\x7flS\xd7\x15\xc7?\xd7v\xe2\x17\x12x\xcf\x04X(\xb4\ +vT\n\x94L}\xde\xd6\x1f\x012\x91Pe\xd0LZ\x82\x80\xf2\xa3\x1a\t\xd2\xc6\xd0\xa4)\ +\xc9\xb4\x9f\x19\x1bN\xd6N\x1bRGZ\r\xb6vR\x15\xa6u\xb0I[b\x952\xf6C\xc2\xd01\xca\ +"Z\xa7\x82\xb2-lq\x10\xac\x89`\xf2{4\x89\xed8\xf1\xdd\x1f\xd7v\xe2\xfc\xa2H\xed\ +\x8ed\xbd\xfb\xae\xcf\xbd\xe7{\xce=\xdfs\xcf\x13\xcc,K\x97\x15\xf3\x9f#_\x04\xa3\ +\xd0Ar\\b\xc5\x04\xef\xf4\xbb\xa8*\x1b\xe7\xd0\xeb\xc5\xa9\x84X\xc4\xdem\x15\x94\ +,6p9\x1d\xa2x\xa1!*w>\x0b\xc0\xfaG\x1f>\xfa\xe7\xf3=G\xe2\xf1\xd1w\xb9\x8b\x88\x99\ +&\x1f{\x90\xe1\xf2\x95D?\xf7\x04K\x8cB\xf2\x1c\x0ep:@\x02\x89\xa4@\xcb\x93\x9c\xbc\ +\x94\xc7\xf9\xde"\xecQCn\xaaX!?\xfeP\tK\x97\xadHY\xc3Iy<x>\x15\xfc\xe3E\xc7\x9d\xf7\ +G6\x00\x17\xee\x15\x80o\xdf\x93\\\xae-\xc7Y\xe2A\x13\x0e@\x82\xc3\x91\xab4:\x06\xe3\ +)\xc1\xa0\r{\x8f\x14\x12\xb5\x87\xd8\xb3}\xe3\xf8W\xf7\xd5\x8e\xcf\x9b\xa7\xc5\xae\ +\xfc\xfbfd\xcb\xe7\xdbL`3\xf0\x87{\x01\xb0rc\x19\xe1gw\x93\xafi8\xa7\x1a\x9e,\xef\x8f\ +\xc0_\xae@b\x0c\xd6\xafv\xf2\xf4\x0b\x05D\xed!\xaew\xbf\xc2B\xbd\x88\xdb##\xf8\xfc\r\ +\x00\x07\x81\xb6\x0f\x04\xa0r\r\xe3\xa6\x8f\xe4\x9e\x8d\xe4;\x1c\x08!f\x07p\xee2|\xaa\ +\x14\x9c.\x88\xc7\xc1\x99\x0f\xd7n\xeb\x1c\xbb\xe8\xa5\xe5+;\xa8./\xa3\xdf\xb2x\xb7\ +\xe7_\xd4\xd4\x7f?\x04TM\xddc\x9a\x7f+\xefCT\x9bs\x1b\xbfz\x03~z\x1a\x8a\xe7\x83p\x80\ +\xe6\x06C\x87\xf9\x05\xf0\x89\xfbm\xda\xb7\xbd\xc3gv~\x87`\xe8m\xbc\x86\xc1\x1a\xf3A\ +\xc2\xa7\x0fW\xd6\x98\xc8\xbb\x02\xb0c\xe0\xce\x03)\xd5o&y\xeb\x1al}\x0c\x96\x190\xaf\ +`f\x9d\x1a\x13\xea\x1aZ\t\xbd\x19\xc6k\x18\x18%\x1e\xbe\x11\x080\x15\x84s\xf2\xcb\xaa\ +\xfb\x90;\xd6#|\x1fC\xb8\\\n@&\n\x89$<\xfa5\xb8u\x07\x9e\xa9\x00-\x0f\x16,\x98\xfdx\x9e\ +\xf4\xc3\xf5\x018p$DU\xe5\x13\x98\xbe\xa5\x88\x02\x8d5\xa5&\xb7\xfa\xce\x06z\x07i\x05pM^T\ +\xb1\x1a\xca\x1e\x80\xfc\xbc)Q\x19\x81\x17O\xc2\x1bm\x8a\x8a.\xd7\xec\x9e\x03\xc8\xb8\xca\ +\xae\xe7v\xa6\xf3\xaa\xae\x19\x19\xe9\xe2\xec\x990\xe1\x7f\xf4s\xaag\x86#x\xe6\xd3\xc8\xe5\ +\xc50\xcf\x9d\xceN1A\xbdC\x9d\xb0c-\x8c\xa7\xd4y\x17hw).\x1a\x087\xfc\xee-\xb0\x13j\xee\x97\ +\x9d!\xaa*L\x1a\xf7\xd7\xd2\xd9\xde\x88"\xf7\xa4\x08\xdc\xf8/|\xa1Z%\x95\x10*\xfc\xf6\x1d\ +\xa8\x0c@(\x00yy\xca\xb8\xd39\xb7\xf1_\x9c\x81\x01\x1b\x12\xa3p`+l]\x07-\x1dP$,\x84t\xe0\ +5t(\xf7g\xf5\xb3\x00\x16\xce\x87"\rR)H\x8e\xc3\xf3A\x05"\xf8Me\xd8\xe5\x9an\xfc\xf2ux\xf5\ +\x1c\xfc\xfe\x12|{+\xdc\xbf\x18\xf6TM\x07\xf5\x83\x06\xf07u\xd0u\xdc\xc7\xf2%~"\xef\xf5\ +\xe7\x1e\xc1S\x9fD.1\x94\x97c)\xd8\xdc\x06\x8f<\x00\r\x1b\xc0(\x84\x94T\x002\xf2\xbd\xe3\ +\xe0o\x86A\x0b\xea\xab\xe0\xe4\x01\xd8Q\x01\xebV\xcd\x9e\x135&X\x03\x11UjK}\x84N\x04\x00\ +\xa4\x0b@\xd7`\xd7Z\xa5\xfc\xd2i\x08~]\x8d\xc7Rp\xcb\x86\xfd?\x87M~\xe5qO\x04\xc2\x87\xa1\ +m\x17\x1fHd|"\n-\x1d\x1d\xd4o\xa9\x03\xc0\xb2\xa2\xea\x08\xb6\xaf\xa3\xfb\xc4_\xe1[\xdb\x95\ +W\xa1\x80Z\xb0\xed\xc7\xb0\xa1\x0c~{A\x19\xfc\xb0\xc4\x8aIt\xb7\x8e\xb9\xba4\x9d\x03)J\x1f\ +\xf1*\xe3Ue\xf0\xfck\xf0\xda\xa5\t\xa3\xdf}\xfa\xde<\x15\x9a\x1ag\x9e\x99\xb9,\xa5\xe36F\ +\x81A\x7f\xd4R\x00.\xf62\x1cKR\x0cp\xe6\xca\x84bK\xc7\xc48\xc3\xdb\xb9"1\xd9Hf,\xa6\xd05\ +\xcb\x7fMG\xdaV\xf62\x92\x9dM\xaa\x96[\xf6\xf4g\x7f\x0c\x0c\r\x8a\xc6\x0b\xd8\xf6\x93\x185\ +\xe6\xc4F\x99q\x8d\t\xf9\x1e\x1fuknR\xe4p1\x94\x1a\x03\xc0\xbb8\t\xa8\xf5\xa0j\xc2\xa1N5\ +\xce\xf7\xf8\xa8\xdc\xe0W\x00\xda\xbf\x94\x8btE~\x11\xd7F\x87X\x91_\x04\xc0\xb5\xd1!\x96\ +\xe7M\x94\xbe\x1b\xc9XV\x07\xc8\xf9\x0f\xc0U\x14#\x1a\x05\xe1\x9c(\xa9\xbf9\x9f$\xdf\xe3\ +\xa3\xfdp\x00\x1d8\x1b\x0e\xf3r{;\xe2`\x89_z\xa4N\xd3\xe0\xd9Y\xbd{\xee)\x03\xcb\x82~\xc70\ +\xe6\x82Bn\x0e+\x0fo\x8f\'X\xe4tgAf@-\xcf+\xe0F2\x96\x03\xec\x957b\xbcz\xa23\x1b\x11[\x18\ +\xec\xae\xadB\xd4\x98\xc8\xea\x9e\xda9\xd2\xcb\x06C\x82\x14\xfc\xc9\x17\xca=\xcb\xb4t|Yy\xba\ +\xc8\xe9\xce\x015\x19\xd8\xd1sC\xfc\xea\xd7\x1d\xe8n=\xdd\x85\x18|\xb6\xb6*\xf72\x9aYtP\ +\xf9B\x06h\xf5\x14\x8d\xe8\xd1t\x98\xcd`\x1a`r\x8a\xc6\x10\x8d\x9b\xbd\xe8\x9a\x91\xbe\xe7\ +\xc1\xb2U5t=\xdeS\xf9\xa1q|6\x80\x00\x11\xad\x07;f\x81\x00\xdd\xadcgJ\xf1\xdf\xcc\x10\xff\ +\x17\x91\x12\x12i\xe3\x899:\xa2\x8fL2\x9d\x8d\x00\x19Sg\xba\xf2a\x13\xd7C%\xd0\xdc\x13\xcc\ +\xea5\xd6\x1a\xbc\x10\xb4\xb2L\xc8\xf6\x8a\x1e\x13]\x02\x11\x03\x12 \xe2"\xfd\xa1\x00z\\\xbf\ +\xab}\xa3\xc4\x0f\x9a\x9e\x05\x11\xe9\x8b\xf0\xcf\xab=\xb8z\x07 t\xa2\t\xdf*?\x08\x1d\x19\ +\xb7in5 n!cQ\xfa\xe3@\xdc\xa6+\x18\xa2\xfb\xed\x10\xa7\xd27i\xe3f\x83\xde\xf7\xac,#\x1a7\x01\ +\xb6\x17OL\xa8Nc\xd0@\xc4\x0c\x10\x12\xdd2\xf0\xe9\x11\xf4L\x85t\x8b\xdc~\xc0\xb7Z5\x08^#\x9d\ +\xf5\xc8\xf4S\xc7\x13\x07\xa4\x8d\xe9k\xc0\xa2\x11\xa1\xa9\xc5r \x82\xc7\xe7#jI\xec\xb8M\xcb\ +\xfe\x06\xa0?\x87\xa6M\xf5&\xed\xc7\xd2/?\x82\xba]6\xc2\xadcM\xca\x01\x97bd\x86i\x82~\xcbJ\x83\ +\xd1A\x82\x8e\x8d\r\x18\x1e\x1dCf;)\xf0y\xb1\xe3Q|\x05\x02[\x83\xd7\x83]\x10\xb7\xc0\xadc\'l\x88\ +\xa9\x8e6\xd0\xac\x12P\xa2\xda4\xb2\xf9?\t\xc0\xee\xfa\x86\xe9\xdf\x87k+\xe9\xbe\x10b_\xf3A\x0c\ +\xc3\xc0W"A\x9b\x08\x9d\xd7\xd0\xb1\x12\x02]7 aa\xc7-\xf4t\xbd\xd7\xdd:\xcc\xd07\xdaq\xb0\xe2\ +\n@t0:\xfb\xc7\xe9d\t_\xe8\x92\x9d\x1d\x01\xba\xdf\x0cO\x07Y\xee\x9fq~\xb2<^YK\xc3\xde-\xb9{^\ +\xed\xe3\xe5\x1f\xb6r\xaa\x07!>J\xe6u\xfc, \xfb\xfe\xdeG\xf7\x99c\xd3\x80\xb7\xbe\x14\x16\x00\ +\xff\x03\x07\x06\x80\xbbd\xd4\xb0\x14\x00\x00\x00\x00IEND\xaeB`\x82' + elif shellName=='SymPySlices': + return \ +'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\ +\x00\x00szz\xf4\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x06bKGD\ +\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00\ +\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd9\n\x10\x10\x0c\ +\x119od\xf7\x00\x00\x07\xa2IDATX\xc3\xb5\x97{PT\xd7\x1d\xc7?wY\x96\xbb\xb0\xcb\ +\xbd\x08\x08\xa8\x19V\x05\xe2\xab\xd95\xc1\x88\xa2\x03\x98\xf1\x11\'-\xda\x98h\ +\x92\xa9b\x1fN:mG\xb0\xb13\xd5v\x80\xc4\xa9m\x98\x04\x8cQcfj \xcdL\x9c\xfc\ +\x03$\xea\x18k\'\xc4\x91\xa8\xa5Iv\x93\xf8H\xa0e7\xf1\x01\xa3v\xef\x85\x95\xbd\ +\xeb\n\xb7\x7f\xdc\xdd\x85\xe5\xa1If\xfa\x9b\xb9s~\xf7\xbc~\xdfs~\xdf\xf3;\xbf\ +#0\xbe\xe4LM\xe7\xea\xbe_\x80\x9cb"<\xa8\xa3\x04\x05>\xf3\x99)\x9b;\xc8\x8bG\ +\xd3\x87BB\x06\x9b\xd7-!;S\xc6\x9c`\x12\xd2\'\xc9B\xe9\x86]\x00\x14\x17\xce\xde\ +\x7f\xb2\xdd\xb3O\xd3n_\xe0\x1e"\x8cW\xb9`&\xb7\x8a\n\xf0\xffh!\x93\xe5\x14\x12\ +M&H0\x81\x0e\x84\xc2\x02b\xa2\xce\x91\x8f\x13i\xef\xb4\xa1\xde\x96\xf5\x95K\xf2\ +\xf4y\xf9\xd9\xe4L\xcd\x1bRn\x85\xf5\xb7[\xdb\x87ZO\x9c3\xf5\xf5\x0f\x94\x00g\ +\xbe+\x00\xc7\x96G\xf8\xa2\xbc\x88\x84\xec4D\xc1\x04\xe8`2\xc5w\xba}\x07\x06\x87\ +\x04zU\xd8\xbc/\x05\xbf\x1a`\xe3\x13\xcb\x06\xb7m)\x1fLN\x16\x83\xe7\xffs\xc5\ +\xbb\xf6\'\xcf;\x81U\xc0\xfb\xdf\x05@\xc1\xb2\xb9\xb8w=\x8dE\x14I\x18mx\xa4\xf4\ +\x0f\xc0\xe9\xf3\x10\xba\x03\xc5\xb3\x12xr\x8f\x15\xbf\x1a\xe0\xeb\x8eCL\x92l\xdc\ +\x18\x18\xc0\xe1\xaa\x00\xa8\x06\x9e\xffV\x00J\xe70\xe8t\x10\xde\xb8\x0c\x8b\xc9\ +\x84 \x08\x13\x038\xf5\x05<4\x1d\x12\xcc\xa0i\x90`\x81\xae\x1b\x12M\xe7r\xd9\xf1\ +\x9b\xf5,/\x9a\x8bOQ\xb8\xe0\xf97\xab7\xbd\xd0\x06\x94\x8d\x9ec\xcc\xfa\n\xa6 ,w\ +\xde\xdd\xf8\xc5\xcbp\xe08\xa4\xdbA0\x81\x98\x04\xb2\x04v+\xcc\xbfO\xa5a\xddg\xac\ +\xd8\xb0\x93\xd6\xb6O\xc9\x95e\xe68g\xe2>^_\xba\xda\x89~O\x00j\x10\x92\x12A\xd7\ +\x8do<\xf9\xa4\x0b\x1e_\x00SeH\xb6\x8e\xdfg\xb5\x13\xd6T\xd4\xd2v\xd6M\xae,#g\xa7\ +\xf1\xbb\x9a\x1aF\x83H\x18\xf9s\xff\x14\xf4\xf5\xc5\x08\x8e,\x04\xb3\xd9\x00\x10\ +\xdd\x85P\x18\n\x9f\x83\xeb}\xf0\xcc\x12\x10\x13!5ub\xf7<\xe2\x82\xaf{\xe0\x0f\xfb\ +\xda(+]\x88\xd3\x91\x83`\x15\x993\xdd\xc9\xf5\xee\x0fk:{\xa9\x1d\xc3\x81\x9f-C\xdf\ +X\x06\xf6d\xc3\xb0\x1ea\xff\x81\xe3p\xf0}\xbe\x97d\xda\xe1z?\xe8\xde\x16\xdeln\xc3\ +\xfd\xa5\x8f\xfa\xd7\x9ac\xb6c.xf)\xfa\xb4tHN\x8a\xb0S0\x8c\xff\xf5\xe4\xf77\x0e\ +\x90\x96S\xc0\x91#Gx\xab\xb9\x8d\xb2%N\xb6>[Ns\xc3V\x8c\xc3=\x02\xc0\xe5\x9bP\xfa\ +\x03\x83T\xd1\xd5+*\xec=:\xf1\xe4\xc9\xc9\xc9$\'\'#\x8ab\xbc\xd1\xb44222\xc8\xcc\ +\xcc\xa4\xae\xae\x0e\xd7\xbc|l\x82\x82\xa0\x9b\xc8\x95e\xe6\x17\xb9b}\xcdQe\x92\x1d\ +l"\x0c\rAx\x10^j\x85\xf6\x8bF\x9b\x1eac \x10\xc0n\xb7\xc7\xd5i\xda\x00CC\x90\x92\ +\x92\x02@__\x1fv\xbb\x9d;\x9a\x1f\xf4A\xd0u\xbe9\xf9s\xd6V\xbeK\xcb\xdb\x0e\xa6Mv\ +\xe1\xbd\xe6\x8b\x07\xf0\xe8\x83\xe8\x93eHL\x84\x81\x10<\xfa\x02\xfc\xf6\x87p\xe6\ +\x12\xdc\xf8\xf2]\x00<\r\x02\xdd\xbd\xf1;\xf0\xf2O\x05\xa6\xa6\x1b\xc0\xa3b\xb7\xdb\ +\xf9|_*C\xe1\xfeX\x9d\xae\x19\xa7B\xe9\xf1\x02.\x1c\xd3\x1d\xb4\x1d\xae\xa1tC\x8dn\ +\x06\x90Dxj\x91\xd1\xf9\xe0qh\xddn\xe8\x8b\x0b\xc0"\x17\x90)\t\xactA\xfb\xa5QL\x7f\ +`|\xd7\x8c6\x0e\xf0\xa7\n\xd8\xd1\xd8\xc8\xa6\xb5k\x00P\x14\xbf\x11\x07\x9eXL\xc7\ +\xe1\x8f\x8c@\xe2\xaa\x82\xcd\xa5\xc6\x80u/\xc3\x81\xbfCj\xd6,N\xd6\xc2\xf6\xb5\xb0\ +n\xf1\xf8\x06\xff\xdb\xff\xedI\xa9\x04u\xa4$\t\xe7\xac\xe9\x11\x17\x0c1\xfd\x81\\\ +\xc3x\xd9\\x\xe9=x\xefcp\xd7\x1b\x03\xfe\xf8\xe4\xf0\xe0\xce\xabF\x99\x95\x95\x157\ +\xa9\xefz\xbc\x11]\x03A\x1c^\xbd0\xc2E\xaa\xa6"[e|~\xc5\x00p\xae\x93[\xc10\xe9\x00\ +\x1f\x9c\x1f\xee\xb8\xa3qX?\xe61\xca\xfc\x1c\xa3,,,$|k\x98\x10\xa3\xb9\x115(\xc4\ +\x1f\x8e\xd8<\x88\x12\xba\xaa\xc4.#\xbd\xb9\xd2p\x81\xa2\x8e-}A\x90E\xb0\rZY\xf7j\ +\x10\x80\xba\xba:f\xcf\x9e\xcd\xfe\x9d\x8fq\xcc\x03yY\xd0\xd5;|b\x9a~e\x01 73\x0c\ +\x18\xe3\x01\xd4\x10\xbc\xd8l\xe8\x964\x07\xa5%.\xe3\x14\xf8\x82\xc6\x17\xd5\xf3,6\ +<=\x01\xf2,6\xf2,\xd0\xa5\x05\xb0%\x0e\xaf\xa4\xa4\xa4\x04\xdb\x95\x16V\x14\xc1\x8a\ +"8y6\x81\xae\xdeA\xa6M\x9bf\x18L\r\xe3\xf7\x83\xef\xba1\xc8\x07\xbc\xd3\x1e\xc6\x92\ +\xe6`\x7fc\r\x12\xf0\xa1\xdb\xcd\xeb\r\r\x98\xaa\xb3]\xd0RB\xe5A8q\x16*\x0f\xc2c{\ +\x03\x9c8k\x94\xfbO\x05(I\x93\t\xde0\x0fgL\x0b\x16p\xb3\xeb4rB"y\x16\x9b\x912\x01\ +\xaa\xaa\xd2\xef\xbf\xcc\x9d\x80\x15\x15\xb0\x99\xcc\xb1\x0f\xe0\x8d\x03\xf5H\xa2\ +\x84,K\x94\x96\x95\x1b\x1c\xe8\xc8r\xb3\xdcSN=\xe5\xe0\x81\xe5Q+Q\xdd\xa3\xd2\xe6\ +\xd3\xd9\xd5w.\xce\x9fK\xb7\x9f\x8aha\xd6/2\x00\xf4\xf7\xf7#Z%D!\x81<\x8b\x8d\x81\ +\xdb:\xa7\xae\xdeb\x95\xc3\x16\t\xef*R\x92\x04\x02H\xa3#\xe1\xc4"\xe1V\xaep\x13-\xae\ +\xb6\x9e\xf2\x98~\xfeL\x0f`\x00\xdc\xfdb=\xd5{\x03\xb16Q\x14\xf9|\xe1C\xb8r\xbcH\xa2\ +\x1c\xb9\xe7AQ\x8dhhz\xd8SzO\x08M\xfc+\xa6\x17\x17\x17\x8fi\xcf\'#\xa6WWW\xc7\xb5}\ +\xd3q\x89\x9d3\x1f\x07Q@\r*\xa8!\xd5pW4!\xf9\xa7\xb3\xed\xae\xc6\xaf\xd1\x17\xf7\xbf\ +{\xf7n\xaeF/\x89\x88X&\xd8\xc8\xbc\xbc<2\xe6\xe5r\xfe\xd0?\x8c\x95\x87@J\x92PCw\xc9\ +\x88\xc6<\x10H\x8d\xdb\xce\xa5K\x97r\xfa\xf7\x7f\x1b\xd3o\x1b%q\xffV\xab\x95\xce\xce\ +N\x94\xaek\xc3\xf7{\xe4\xe0\xebA#\x06\x14\xccvb\xce\xcf\x86*Okl\xe0\xd6r\x99=\xadJ,\ +\xad\x02X\r\xecz\xe3\x13\xe6\xcf\x9fOX\x0bq\xa1\xf3#\x04Y\x88<\x14@\xd2$\xeeC\xe6\xad\ +\x07\xab\xc8\xdd\xf3cf\xcc\x98\xc1\x94)S\xb8\x13\x0c\xd1\x98\xffK\xe3hf\xbb@\x94b \ +\xbc\xdd^\xbe\xba\xe8\xc1\xdc\xd9\x03m\x87+q\xdc\xef\x02AB\xd7T\xaaje\xd0\x14\xf4\xa0\ +\x1f\x9f\x06h*o\xbe\xb2\x8dC\x03*\xaf\xbe\xf3\xa9\x01t\x95L\xe75%\x16\xdd\xb6\xae\x04\ +\xaf\x9aK\xd7\xaf\x9b\xf9\xcaU\x88\xdf\xad\x12\xf0\x86A\xd6\x91\x14\x19\x87\xe4E\x8aF\ +\xc8$!\xfe:v\xcc2\x12\x84\\\xd9`\xbd\xb14\t\x90H\xd3\x00]\xc5\xe9\xa8@A\xe2\xb9Zc\xb0\ +\xde\xe3%\xcd\xe1\xc0\xaf\xe8\xa8\x9a\xca\x8eg+"!\x07j\x9a\xbc\x00Tnr\xd2\xd0\x14A\xf8\ +\x17X\xf3\x94\x8a\x90$\xa1\x8c\xe0\x80\x19@\x06\xa2\x91\xd9\xa7(\x110\x12\xe8 \xa1\xa2\ +\x02r\x9a\x84\xac\xc72)p\xe4\xa2j~\x1cV\x01U\x84\xa3\xad-\xa0)\x90$\x19L\x0f\x1a\x19\ +mM\x15\xa0\xeb\xe8\x80\x904\x92\xff#\x00<\xbd\xa9b\xec\xfbpQ)\x1dg\xda\xd8RU\x8d,\xcb\ +8\xb2u\x10\x87\xb7.W\x96PB\x02\x92$CHA\xd5\x14\xa4H\xbc\x97\x92$\x10\xc7\x12Z\xd5@\xd1\ +\x0c\x00\xfe^\xff\xc4\x8f\xd3\x91\xe2>\xd3\xa277\xd6\xd0q\xd6=\x16d\x91k\xdc\xfa\x91\ +\xf2pi9\x15\x9b\xd7\xc6\xcfy\xb1\x9b\xd7\xff\\\xcb1\x0f\x82\xc0\xffQ\x1a_\xab\xd1\xbb\ +/u\xd3\xf1A\xd3\x18\xe0\xb5\x07\xdd\x02\xc0\xff\x00+\xe4\xca\x93\x95`\xa8\xfa\x00\x00\ +\x00\x00IEND\xaeB`\x82' diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/interpreter.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/interpreter.py new file mode 100644 index 0000000..35468a1 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/interpreter.py @@ -0,0 +1,170 @@ +"""Interpreter executes Python commands.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com> / " +__author__ += "David N. Mashburn <david.n.mashburn@gmail.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import os +import sys +from code import InteractiveInterpreter, compile_command +import dispatcher +import introspect +import wx + +class Interpreter(InteractiveInterpreter): + """Interpreter based on code.InteractiveInterpreter.""" + + revision = __revision__ + + def __init__(self, locals=None, rawin=None, + stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr, + showInterpIntro=True): + """Create an interactive interpreter object.""" + InteractiveInterpreter.__init__(self, locals=locals) + self.stdin = stdin + self.stdout = stdout + self.stderr = stderr + if rawin: + import __builtin__ + __builtin__.raw_input = rawin + del __builtin__ + if showInterpIntro: + copyright = 'Type "help", "copyright", "credits" or "license"' + copyright += ' for more information.' + self.introText = 'Python %s on %s%s%s' % \ + (sys.version, sys.platform, os.linesep, copyright) + try: + sys.ps1 + except AttributeError: + sys.ps1 = '>>> ' + try: + sys.ps2 + except AttributeError: + sys.ps2 = '... ' + self.more = 0 + # List of lists to support recursive push(). + self.commandBuffer = [] + self.startupScript = None + + def push(self, command, astMod=None): + """Send command to the interpreter to be executed. + + Because this may be called recursively, we append a new list + onto the commandBuffer list and then append commands into + that. If the passed in command is part of a multi-line + command we keep appending the pieces to the last list in + commandBuffer until we have a complete command. If not, we + delete that last list.""" + + # In case the command is unicode try encoding it + if type(command) == unicode: + try: + command = command.encode(wx.GetDefaultPyEncoding()) + except UnicodeEncodeError: + pass # otherwise leave it alone + + if not self.more: + try: del self.commandBuffer[-1] + except IndexError: pass + if not self.more: self.commandBuffer.append([]) + self.commandBuffer[-1].append(command) + source = '\n'.join(self.commandBuffer[-1]) + + # If an ast code module is passed, pass it to runModule instead + more=False + if astMod != None: + self.runModule(astMod) + self.more=False + else: + more = self.more = self.runsource(source) + dispatcher.send(signal='Interpreter.push', sender=self, + command=command, more=more, source=source) + return more + + def runsource(self, source): + """Compile and run source code in the interpreter.""" + stdin, stdout, stderr = sys.stdin, sys.stdout, sys.stderr + sys.stdin, sys.stdout, sys.stderr = \ + self.stdin, self.stdout, self.stderr + more = InteractiveInterpreter.runsource(self, source) + # this was a cute idea, but didn't work... + #more = self.runcode(compile(source,'', + # ('exec' if self.useExecMode else 'single'))) + + + # If sys.std* is still what we set it to, then restore it. + # But, if the executed source changed sys.std*, assume it was + # meant to be changed and leave it. Power to the people. + if sys.stdin == self.stdin: + sys.stdin = stdin + else: + self.stdin = sys.stdin + if sys.stdout == self.stdout: + sys.stdout = stdout + else: + self.stdout = sys.stdout + if sys.stderr == self.stderr: + sys.stderr = stderr + else: + self.stderr = sys.stderr + return more + + def runModule(self, mod): + """Compile and run an ast module in the interpreter.""" + stdin, stdout, stderr = sys.stdin, sys.stdout, sys.stderr + sys.stdin, sys.stdout, sys.stderr = \ + self.stdin, self.stdout, self.stderr + self.runcode(compile(mod,'','single')) + # If sys.std* is still what we set it to, then restore it. + # But, if the executed source changed sys.std*, assume it was + # meant to be changed and leave it. Power to the people. + if sys.stdin == self.stdin: + sys.stdin = stdin + else: + self.stdin = sys.stdin + if sys.stdout == self.stdout: + sys.stdout = stdout + else: + self.stdout = sys.stdout + if sys.stderr == self.stderr: + sys.stderr = stderr + else: + self.stderr = sys.stderr + return False + + def getAutoCompleteKeys(self): + """Return list of auto-completion keycodes.""" + return [ord('.')] + + def getAutoCompleteList(self, command='', *args, **kwds): + """Return list of auto-completion options for a command. + + The list of options will be based on the locals namespace.""" + stdin, stdout, stderr = sys.stdin, sys.stdout, sys.stderr + sys.stdin, sys.stdout, sys.stderr = \ + self.stdin, self.stdout, self.stderr + l = introspect.getAutoCompleteList(command, self.locals, + *args, **kwds) + sys.stdin, sys.stdout, sys.stderr = stdin, stdout, stderr + return l + + def getCallTip(self, command='', *args, **kwds): + """Return call tip text for a command. + + Call tip information will be based on the locals namespace.""" + return introspect.getCallTip(command, self.locals, *args, **kwds) + + +class InterpreterAlaCarte(Interpreter): + """Demo Interpreter.""" + + def __init__(self, locals, rawin, stdin, stdout, stderr, + ps1='main prompt', ps2='continuation prompt'): + """Create an interactive interpreter object.""" + Interpreter.__init__(self, locals=locals, rawin=rawin, + stdin=stdin, stdout=stdout, stderr=stderr) + sys.ps1 = ps1 + sys.ps2 = ps2 + + diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/introspect.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/introspect.py new file mode 100644 index 0000000..0a14f89 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/introspect.py @@ -0,0 +1,389 @@ +"""Provides a variety of introspective-type support functions for +things like call tips and command auto completion.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import cStringIO +import inspect +import sys +import tokenize +import types +import wx + +def getAutoCompleteList(command='', locals=None, includeMagic=1, + includeSingle=1, includeDouble=1): + """Return list of auto-completion options for command. + + The list of options will be based on the locals namespace.""" + attributes = [] + # Get the proper chunk of code from the command. + root = getRoot(command, terminator='.') + try: + if locals is not None: + object = eval(root, locals) + else: + object = eval(root) + except: + pass + else: + attributes = getAttributeNames(object, includeMagic, + includeSingle, includeDouble) + return attributes + +def getAttributeNames(object, includeMagic=1, includeSingle=1, + includeDouble=1): + """Return list of unique attributes, including inherited, for object.""" + attributes = [] + dict = {} + if not hasattrAlwaysReturnsTrue(object): + # Add some attributes that don't always get picked up. + special_attrs = ['__bases__', '__class__', '__dict__', '__name__', + 'func_closure', 'func_code', 'func_defaults', + 'func_dict', 'func_doc', 'func_globals', 'func_name'] + attributes += [attr for attr in special_attrs \ + if hasattr(object, attr)] + if includeMagic: + try: attributes += object._getAttributeNames() + except: pass + # Special code to allow traits to be caught by autocomplete + if hasattr(object,'trait_get'): + try: + for i in object.trait_get().keys(): + if i not in attributes: + if hasattr(object, i): + attributes += i + except: + pass + # Get all attribute names. + str_type = str(type(object)) + if str_type == "<type 'array'>": + attributes += dir(object) + else: + attrdict = getAllAttributeNames(object) + # Store the object's dir. + object_dir = dir(object) + for (obj_type_name, technique, count), attrlist in attrdict.items(): + # This complexity is necessary to avoid accessing all the + # attributes of the object. This is very handy for objects + # whose attributes are lazily evaluated. + if type(object).__name__ == obj_type_name and technique == 'dir': + attributes += attrlist + else: + attributes += [attr for attr in attrlist \ + if attr not in object_dir and hasattr(object, attr)] + + # Remove duplicates from the attribute list. + for item in attributes: + dict[item] = None + attributes = dict.keys() + # new-style swig wrappings can result in non-string attributes + # e.g. ITK http://www.itk.org/ + attributes = [attribute for attribute in attributes \ + if type(attribute) == str] + attributes.sort(lambda x, y: cmp(x.upper(), y.upper())) + if not includeSingle: + attributes = filter(lambda item: item[0]!='_' \ + or item[1:2]=='_', attributes) + if not includeDouble: + attributes = filter(lambda item: item[:2]!='__', attributes) + return attributes + +def hasattrAlwaysReturnsTrue(object): + return hasattr(object, 'bogu5_123_aTTri8ute') + +def getAllAttributeNames(object): + """Return dict of all attributes, including inherited, for an object. + + Recursively walk through a class and all base classes. + """ + attrdict = {} # (object, technique, count): [list of attributes] + # !!! + # Do Not use hasattr() as a test anywhere in this function, + # because it is unreliable with remote objects: xmlrpc, soap, etc. + # They always return true for hasattr(). + # !!! + try: + # This could(?) fail if the type is poorly defined without + # even a name. + key = type(object).__name__ + except: + key = 'anonymous' + # Wake up sleepy objects - a hack for ZODB objects in "ghost" state. + wakeupcall = dir(object) + del wakeupcall + # Get attributes available through the normal convention. + attributes = dir(object) + attrdict[(key, 'dir', len(attributes))] = attributes + # Get attributes from the object's dictionary, if it has one. + try: + attributes = object.__dict__.keys() + attributes.sort() + except: # Must catch all because object might have __getattr__. + pass + else: + attrdict[(key, '__dict__', len(attributes))] = attributes + # For a class instance, get the attributes for the class. + try: + klass = object.__class__ + except: # Must catch all because object might have __getattr__. + pass + else: + if klass is object: + # Break a circular reference. This happens with extension + # classes. + pass + else: + attrdict.update(getAllAttributeNames(klass)) + # Also get attributes from any and all parent classes. + try: + bases = object.__bases__ + except: # Must catch all because object might have __getattr__. + pass + else: + if isinstance(bases, types.TupleType): + for base in bases: + if type(base) is types.TypeType: + # Break a circular reference. Happens in Python 2.2. + pass + else: + attrdict.update(getAllAttributeNames(base)) + return attrdict + +def getCallTip(command='', locals=None): + """For a command, return a tuple of object name, argspec, tip text. + + The call tip information will be based on the locals namespace.""" + calltip = ('', '', '') # object name, argspec, tip text. + # Get the proper chunk of code from the command. + root = getRoot(command, terminator='(') + try: + if locals is not None: + object = eval(root, locals) + else: + object = eval(root) + except: + return calltip + name = '' + object, dropSelf = getBaseObject(object) + try: + name = object.__name__ + except AttributeError: + pass + tip1 = '' + argspec = '' + if inspect.isbuiltin(object): + # Builtin functions don't have an argspec that we can get. + pass + elif inspect.isfunction(object): + # tip1 is a string like: "getCallTip(command='', locals=None)" + argspec = apply(inspect.formatargspec, inspect.getargspec(object)) + if dropSelf: + # The first parameter to a method is a reference to an + # instance, usually coded as "self", and is usually passed + # automatically by Python; therefore we want to drop it. + temp = argspec.split(',') + if len(temp) == 1: # No other arguments. + argspec = '()' + elif temp[0][:2] == '(*': # first param is like *args, not self + pass + else: # Drop the first argument. + argspec = '(' + ','.join(temp[1:]).lstrip() + tip1 = name + argspec + doc = '' + if callable(object): + try: + doc = inspect.getdoc(object) + except: + pass + if doc: + # tip2 is the first separated line of the docstring, like: + # "Return call tip text for a command." + # tip3 is the rest of the docstring, like: + # "The call tip information will be based on ... <snip> + firstline = doc.split('\n')[0].lstrip() + if tip1 == firstline or firstline[:len(name)+1] == name+'(': + tip1 = '' + else: + tip1 += '\n\n' + docpieces = doc.split('\n\n') + tip2 = docpieces[0] + tip3 = '\n\n'.join(docpieces[1:]) + tip = '%s%s\n\n%s' % (tip1, tip2, tip3) + else: + tip = tip1 + calltip = (name, argspec[1:-1], tip.strip()) + return calltip + +def getRoot(command, terminator=None): + """Return the rightmost root portion of an arbitrary Python command. + + Return only the root portion that can be eval()'d without side + effects. The command would normally terminate with a '(' or + '.'. The terminator and anything after the terminator will be + dropped.""" + command = command.split('\n')[-1] + if command.startswith(sys.ps2): + command = command[len(sys.ps2):] + command = command.lstrip() + command = rtrimTerminus(command, terminator) + tokens = getTokens(command) + if not tokens: + return '' + if tokens[-1][0] is tokenize.ENDMARKER: + # Remove the end marker. + del tokens[-1] + if not tokens: + return '' + if terminator == '.' and \ + (tokens[-1][1] <> '.' or tokens[-1][0] is not tokenize.OP): + # Trap decimals in numbers, versus the dot operator. + return '' + else: + # Strip off the terminator. + if terminator and command.endswith(terminator): + size = 0 - len(terminator) + command = command[:size] + command = command.rstrip() + tokens = getTokens(command) + tokens.reverse() + line = '' + start = None + prefix = '' + laststring = '.' + emptyTypes = ('[]', '()', '{}') + for token in tokens: + tokentype = token[0] + tokenstring = token[1] + line = token[4] + if tokentype is tokenize.ENDMARKER: + continue + if tokentype in (tokenize.NAME, tokenize.STRING, tokenize.NUMBER) \ + and laststring != '.': + # We've reached something that's not part of the root. + if prefix and line[token[3][1]] != ' ': + # If it doesn't have a space after it, remove the prefix. + prefix = '' + break + if tokentype in (tokenize.NAME, tokenize.STRING, tokenize.NUMBER) \ + or (tokentype is tokenize.OP and tokenstring == '.'): + if prefix: + # The prefix isn't valid because it comes after a dot. + prefix = '' + break + else: + # start represents the last known good point in the line. + start = token[2][1] + elif len(tokenstring) == 1 and tokenstring in ('[({])}'): + # Remember, we're working backwords. + # So prefix += tokenstring would be wrong. + if prefix in emptyTypes and tokenstring in ('[({'): + # We've already got an empty type identified so now we + # are in a nested situation and we can break out with + # what we've got. + break + else: + prefix = tokenstring + prefix + else: + # We've reached something that's not part of the root. + break + laststring = tokenstring + if start is None: + start = len(line) + root = line[start:] + if prefix in emptyTypes: + # Empty types are safe to be eval()'d and introspected. + root = prefix + root + return root + +def getTokens(command): + """Return list of token tuples for command.""" + + # In case the command is unicode try encoding it + if type(command) == unicode: + try: + command = command.encode(wx.GetDefaultPyEncoding()) + except UnicodeEncodeError: + pass # otherwise leave it alone + + f = cStringIO.StringIO(command) + # tokens is a list of token tuples, each looking like: + # (type, string, (srow, scol), (erow, ecol), line) + tokens = [] + # Can't use list comprehension: + # tokens = [token for token in tokenize.generate_tokens(f.readline)] + # because of need to append as much as possible before TokenError. + try: +## This code wasn't backward compatible with Python 2.1.3. +## +## for token in tokenize.generate_tokens(f.readline): +## tokens.append(token) + + # This works with Python 2.1.3 (with nested_scopes). + def eater(*args): + tokens.append(args) + tokenize.tokenize_loop(f.readline, eater) + except tokenize.TokenError: + # This is due to a premature EOF, which we expect since we are + # feeding in fragments of Python code. + pass + return tokens + +def rtrimTerminus(command, terminator=None): + """Return command minus anything that follows the final terminator.""" + if terminator: + pieces = command.split(terminator) + if len(pieces) > 1: + command = terminator.join(pieces[:-1]) + terminator + return command + +def getBaseObject(object): + """Return base object and dropSelf indicator for an object.""" + if inspect.isbuiltin(object): + # Builtin functions don't have an argspec that we can get. + dropSelf = 0 + elif inspect.ismethod(object): + # Get the function from the object otherwise + # inspect.getargspec() complains that the object isn't a + # Python function. + try: + if object.im_self is None: + # This is an unbound method so we do not drop self + # from the argspec, since an instance must be passed + # as the first arg. + dropSelf = 0 + else: + dropSelf = 1 + object = object.im_func + except AttributeError: + dropSelf = 0 + elif inspect.isclass(object): + # Get the __init__ method function for the class. + constructor = getConstructor(object) + if constructor is not None: + object = constructor + dropSelf = 1 + else: + dropSelf = 0 + elif callable(object): + # Get the __call__ method instead. + try: + object = object.__call__.im_func + dropSelf = 1 + except AttributeError: + dropSelf = 0 + else: + dropSelf = 0 + return object, dropSelf + +def getConstructor(object): + """Return constructor for class object, or None if there isn't one.""" + try: + return object.__init__.im_func + except AttributeError: + for base in object.__bases__: + constructor = getConstructor(base) + if constructor is not None: + return constructor + return None diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/magic.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/magic.py new file mode 100644 index 0000000..9625f87 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/magic.py @@ -0,0 +1,92 @@ +"""magic.py is a utility that allows a simple line from the interpreter +be translated from a more bash-like form to a python form. +For instance, 'plot a' is transformed to 'plot(a)' +Special exceptions are made for predefined ls,cd, and pwd functions""" + +__author__ = "David N. Mashburn <david.n.mashburn@gmail.com>" +# created 07/01/2009 + +import keyword + +from parse import testForContinuations + +aliasDict = {} + +#DNM +# TODO : Still Refining this... seems to be ok for now... still finding gotchas, though! +# TODO : Multi-line strings seem to be correctly broken into commands by PyCrust(PySlices) +# TODO : Is there a better version of ls, cd, pwd, etc that could be used? +def magicSingle(command): + if command=='': # Pass if command is blank + return command + + first_space=command.find(' ') + + if command[0]==' ': # Pass if command begins with a space + pass + elif command[0]=='?': # Do help if starts with ? + command='help('+command[1:]+')' + elif command[0]=='!': # Use os.system if starts with ! + command='sx("'+command[1:]+'")' + elif command in ('ls','pwd'): # automatically use ls and pwd with no arguments + command=command+'()' + elif command[:3] in ('ls ','cd '): # when using the 'ls ' or 'cd ' constructs, fill in both parentheses and quotes + command=command[:2]+'("'+command[3:]+'")' + elif command[:6] == 'alias ': + c = command[6:].lstrip().split(' ') + if len(c)<2: + #print 'Not enough arguments for alias!' + command = '' + else: + n,v = c[0],' '.join(c[1:]) + aliasDict[n]=v + command = '' + elif command.split(' ')[0] in aliasDict.keys(): + c = command.split(' ') + if len(c)<2: + command = 'sx("'+aliasDict[c[0]]+'")' + else: + command = 'sx("'+aliasDict[c[0]]+' '+' '.join(c[1:])+'")' + elif first_space!=-1: # if there is at least one space, add parentheses at beginning and end + cmds=command.split(' ') + if len(cmds)>1: + wd1=cmds[0] + wd2=cmds[1] + i=1 + while wd2=='': + i+=1 + if len(cmds)==i: + break + wd2=cmds[i] + if wd2=='': + return command + if (wd1[0].isalpha() or wd1[0]=='_') and (wd2[0].isalnum() or (wd2[0] in """."'_""")) and not keyword.iskeyword(wd1) and not keyword.iskeyword(wd2): + if wd1.replace('.','').replace('_','').isalnum(): + command=wd1+'('+command[(first_space+1):]+')' # add parentheses where the first space was and at the end... hooray! + return command + +def magic(command): + continuations = testForContinuations(command) + + if len(continuations)==2: # Error case... + return command + elif len(continuations)==4: + stringContinuationList,indentationBlockList, \ + lineContinuationList,parentheticalContinuationList = continuations + + commandList=[] + firstLine = True + for i in command.split('\n'): + if firstLine: + commandList.append(magicSingle(i)) + elif stringContinuationList.pop(0)==False and \ + indentationBlockList.pop(0)==False and \ + lineContinuationList.pop(0)==False and \ + parentheticalContinuationList.pop(0)==False: + commandList.append(magicSingle(i)) # unless this is in a larger expression, use magic + else: + commandList.append(i) + + firstLine=False + + return '\n'.join(commandList) diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/parse.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/parse.py new file mode 100644 index 0000000..bb6c819 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/parse.py @@ -0,0 +1,128 @@ +"""parse.py is a utility that allows simple checking for line continuations +to give the shell information about where text is commented and where it is +not and also how to appropriately break up a sequence of commands into +separate multi-line commands... +""" + +__author__ = "David N. Mashburn <david.n.mashburn@gmail.com>" +# created 12/20/2009 + +import re + +# change this to testForContinuations + +def testForContinuations(codeBlock,ignoreErrors=False): + """ Test 4 different types of continuations:""" + \ + """ String Continuations (ie with ''')""" + \ + """ Indentation Block Continuations (ie "if 1:" )""" + \ + """ Line Continuations (ie with \\ character )""" + \ + """ Parenthetical continuations (, [, or {""" + + stringMark = None + paraList = [] + indentNumber=[0] + + stringMarks = ['"""',"'''",'"',"'"] + openMarks = ['(','[','{'] + closeMarks = [')',']','}'] + paraMarkDict = { '(':')', '[':']', '{':'}' } + + stringContinuationList=[] + lineContinuationList=[] # For \ continuations ... False because cannot start as line Continuation... + indentationBlockList=[] + parentheticalContinuationList=[] + newIndent=False + lspContinuation=False + for i,l in enumerate(codeBlock.split('\n')): + currentIndentation = len(l)-len(l.lstrip()) + + if i>0: + lspContinuation = lineContinuationList[-1] or \ + stringContinuationList[-1] or \ + parentheticalContinuationList[-1] + # first, check for non-executing lines (whitespace and/or comments only) + if l.lstrip()=='': + emptyLine=True + elif l.strip()[0]=='#': + emptyLine=True + else: # otherwise, check the indentation... + emptyLine=False + if newIndent and currentIndentation>indentNumber[-1]: + newIndent=False + indentNumber.append(currentIndentation) + elif lspContinuation: + pass + elif not newIndent and currentIndentation in indentNumber: + while currentIndentation<indentNumber[-1]: + indentNumber.pop() # This is the end of an indentation block + elif not ignoreErrors: + #print 'Invalid Indentation!!' + return ['Invalid Indentation Error',i] + + firstWord = re.match(' *\w*',l).group().lstrip() + if firstWord in ['if','else','elif','for','while', + 'def','class','try','except','finally']: + hasContinuationWord = True + else: + hasContinuationWord = False + + + commented=False + nonCommentLength=len(l) + + result = re.finditer('"""'+'|'+"'''" + r'''|"|'|\"|\'|\(|\)|\[|\]|\{|\}|#''',l) + for r in result: + j = r.group() + + if stringMark == None: + if j=='#': # If it is a legitimate comment, ignore everything after + commented=True + # get length up to last non-comment character + nonCommentLength = r.start() + break + elif j in stringMarks: + stringMark=j + else: + if paraList != [] and j in closeMarks: + if paraMarkDict[paraList[-1]]==j: + paraList.pop() + elif not ignoreErrors: + #print 'Invalid Syntax!!' + return ['Invalid Syntax Error',i] + if j in openMarks: + paraList.append(j) + elif stringMark==j: + stringMark=None + + stringContinuationList.append(stringMark!=None) + + indentationBlockList.append(False) + nonCommentString = l[:nonCommentLength].rstrip() + if nonCommentString!='' and stringContinuationList[-1]==False: + if nonCommentString[-1]==':': + indentationBlockList[-1]=True + newIndent=True + + lineContinuationList.append(False) + if len(l)>0 and not commented: + if l[-1]=='\\': + lineContinuationList[-1]=True + + parentheticalContinuationList.append( paraList != [] ) + + # Now stringContinuationList (et al) is line by line key for magic + # telling it whether or not each next line is part of a string continuation + + if (stringContinuationList[-1] or indentationBlockList[-1] or \ + lineContinuationList[-1] or parentheticalContinuationList[-1]) \ + and not ignoreErrors: + #print 'Incomplete Syntax!!' + return ['Incomplete Syntax Error',i] + + if newIndent and not ignoreErrors: + #print 'Incomplete Indentation!' + return ['Incomplete Indentation Error',i] + + # Note that if one of these errors above gets thrown, the best solution is to pass the resulting block + # to the interpreter as exec instead of interp + return stringContinuationList,indentationBlockList,lineContinuationList,parentheticalContinuationList diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/path.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/path.py new file mode 100644 index 0000000..ccc8177 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/path.py @@ -0,0 +1,36 @@ +"""path.py is a utility containing some very simple path utilities for Py to use""" + +__author__ = "David N. Mashburn <david.n.mashburn@gmail.com>" +# 07/01/2009 + +import os +import glob +import commands + +def pwd(): + print os.getcwd() + +def cd(path,usePrint=True): + os.chdir(os.path.expandvars(os.path.expanduser(path))) + if usePrint: + pwd() + +def ls(str='*',fullpath=False): + g=glob.glob(os.path.expandvars(os.path.expanduser(str))) + if fullpath: + for i in g: + print i + else: + for i in g: + print os.path.split(i)[1] + +# This prints the results of running a command in the GUI shell but be warned! +# This is a blocking call, and if you open any kind of interactive +# command-line program like python or bash, the shell will permanantly +# freeze! +# If you want this kind of behavior to be available, please use ipython +# This is NOT a feature or goal of the Py project! +def sx(str=''): + print commands.getoutput(str) + +#cd('~',usePrint=False) diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/pseudo.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/pseudo.py new file mode 100644 index 0000000..482be6c --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/pseudo.py @@ -0,0 +1,101 @@ +"""Provides a variety of classes to create pseudo keywords and pseudo files.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + + +class PseudoKeyword: + """A callable class that calls a method passed as a parameter. + + Good for creating a pseudo keyword in the python runtime + environment. The keyword is really an object that has a repr() + that calls itself which calls the method that was passed in the + init of the object. All this just to avoid having to type in the + closing parens on a method. So, for example: + + >>> quit = PseudoKeyword(SomeObject.someMethod) + >>> quit + + SomeObject.someMethod gets executed as if it had been called + directly and the user didn't have to type the parens, like + 'quit()'. This technique is most applicable for pseudo keywords + like quit, exit and help. + + If SomeObject.someMethod can take parameters, they can still be + passed by using the keyword in the traditional way with parens.""" + + def __init__(self, method): + """Create a callable object that executes method when called.""" + + if callable(method): + self.method = method + else: + raise ValueError, 'method must be callable' + + def __call__(self, *args, **kwds): + self.method(*args, **kwds) + + def __repr__(self): + self() + return '' + + +class PseudoFile: + + def __init__(self): + """Create a file-like object.""" + pass + + def readline(self): + pass + + def write(self, s): + pass + + def writelines(self, l): + map(self.write, l) + + def flush(self): + pass + + def isatty(self): + pass + + +class PseudoFileIn(PseudoFile): + + def __init__(self, readline, readlines=None): + if callable(readline): + self.readline = readline + else: + raise ValueError, 'readline must be callable' + if callable(readlines): + self.readlines = readlines + + def isatty(self): + return 1 + + +class PseudoFileOut(PseudoFile): + + def __init__(self, write): + if callable(write): + self.write = write + else: + raise ValueError, 'write must be callable' + + def isatty(self): + return 1 + + +class PseudoFileErr(PseudoFile): + + def __init__(self, write): + if callable(write): + self.write = write + else: + raise ValueError, 'write must be callable' + + def isatty(self): + return 1 diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/shell.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/shell.py new file mode 100644 index 0000000..1009f6a --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/shell.py @@ -0,0 +1,1575 @@ +"""Shell is an interactive text control in which a user types in +commands to be sent to the interpreter. This particular shell is +based on wxPython's wxStyledTextCtrl. + +Sponsored by Orbtech - Your source for Python programming expertise.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import wx +from wx import stc + +import keyword +import os +import sys +import time + +from buffer import Buffer +import dispatcher +import editwindow +import frame +from pseudo import PseudoFileIn +from pseudo import PseudoFileOut +from pseudo import PseudoFileErr +from version import VERSION +from magic import magic +from path import ls,cd,pwd,sx + +sys.ps3 = '<-- ' # Input prompt. +USE_MAGIC=True +# Force updates from long-running commands after this many seconds +PRINT_UPDATE_MAX_TIME=2 + +NAVKEYS = (wx.WXK_END, wx.WXK_LEFT, wx.WXK_RIGHT, + wx.WXK_UP, wx.WXK_DOWN, wx.WXK_PRIOR, wx.WXK_NEXT) + + +class ShellFrame(frame.Frame, frame.ShellFrameMixin): + """Frame containing the shell component.""" + + name = 'Shell Frame' + revision = __revision__ + + def __init__(self, parent=None, id=-1, title='PyShell', + pos=wx.DefaultPosition, size=wx.DefaultSize, + style=wx.DEFAULT_FRAME_STYLE, locals=None, + InterpClass=None, + config=None, dataDir=None, + *args, **kwds): + """Create ShellFrame instance.""" + frame.Frame.__init__(self, parent, id, title, pos, size, style) + frame.ShellFrameMixin.__init__(self, config, dataDir) + + if size == wx.DefaultSize: + self.SetSize((750, 525)) + + intro = 'PyShell %s - The Flakiest Python Shell' % VERSION + self.SetStatusText(intro.replace('\n', ', ')) + self.shell = Shell(parent=self, id=-1, introText=intro, + locals=locals, InterpClass=InterpClass, + startupScript=self.startupScript, + execStartupScript=self.execStartupScript, + *args, **kwds) + + # Override the shell so that status messages go to the status bar. + self.shell.setStatusText = self.SetStatusText + + self.shell.SetFocus() + self.LoadSettings() + + + def OnClose(self, event): + """Event handler for closing.""" + # This isn't working the way I want, but I'll leave it for now. + if self.shell.waiting: + if event.CanVeto(): + event.Veto(True) + else: + self.SaveSettings() + self.shell.destroy() + self.Destroy() + + def OnAbout(self, event): + """Display an About window.""" + title = 'About PyShell' + text = 'PyShell %s\n\n' % VERSION + \ + 'Yet another Python shell, only flakier.\n\n' + \ + 'Half-baked by Patrick K. O\'Brien,\n' + \ + 'the other half is still in the oven.\n\n' + \ + 'Shell Revision: %s\n' % self.shell.revision + \ + 'Interpreter Revision: %s\n\n' % self.shell.interp.revision + \ + 'Platform: %s\n' % sys.platform + \ + 'Python Version: %s\n' % sys.version.split()[0] + \ + 'wxPython Version: %s\n' % wx.VERSION_STRING + \ + ('\t(%s)\n' % ", ".join(wx.PlatformInfo[1:])) + dialog = wx.MessageDialog(self, text, title, + wx.OK | wx.ICON_INFORMATION) + dialog.ShowModal() + dialog.Destroy() + + + def OnHelp(self, event): + """Show a help dialog.""" + frame.ShellFrameMixin.OnHelp(self, event) + + + def LoadSettings(self): + if self.config is not None: + frame.ShellFrameMixin.LoadSettings(self) + frame.Frame.LoadSettings(self, self.config) + self.shell.LoadSettings(self.config) + + def SaveSettings(self, force=False): + if self.config is not None: + frame.ShellFrameMixin.SaveSettings(self) + if self.autoSaveSettings or force: + frame.Frame.SaveSettings(self, self.config) + self.shell.SaveSettings(self.config) + + def DoSaveSettings(self): + if self.config is not None: + self.SaveSettings(force=True) + self.config.Flush() + + + + +HELP_TEXT = """\ +* Key bindings: +Home Go to the beginning of the command or line. +Shift+Home Select to the beginning of the command or line. +Shift+End Select to the end of the line. +End Go to the end of the line. +Ctrl+C Copy selected text, removing prompts. +Ctrl+Shift+C Copy selected text, retaining prompts. +Alt+C Copy to the clipboard, including prefixed prompts. +Ctrl+X Cut selected text. +Ctrl+V Paste from clipboard. +Ctrl+Shift+V Paste and run multiple commands from clipboard. +Ctrl+Up Arrow Retrieve Previous History item. +Alt+P Retrieve Previous History item. +Ctrl+Down Arrow Retrieve Next History item. +Alt+N Retrieve Next History item. +Shift+Up Arrow Insert Previous History item. +Shift+Down Arrow Insert Next History item. +F8 Command-completion of History item. + (Type a few characters of a previous command and press F8.) +Ctrl+Enter Insert new line into multiline command. +Ctrl+] Increase font size. +Ctrl+[ Decrease font size. +Ctrl+= Default font size. +Ctrl-Space Show Auto Completion. +Ctrl-Alt-Space Show Call Tip. +Shift+Enter Complete Text from History. +Ctrl+F Search +F3 Search next +Ctrl+H "hide" lines containing selection / "unhide" +F12 on/off "free-edit" mode +""" + +class ShellFacade: + """Simplified interface to all shell-related functionality. + + This is a semi-transparent facade, in that all attributes of other + are accessible, even though only some are visible to the user.""" + + name = 'Shell Interface' + revision = __revision__ + + def __init__(self, other): + """Create a ShellFacade instance.""" + d = self.__dict__ + d['other'] = other + d['helpText'] = HELP_TEXT + d['this'] = other.this + + def help(self): + """Display some useful information about how to use the shell.""" + self.write(self.helpText) + + def __getattr__(self, name): + if hasattr(self.other, name): + return getattr(self.other, name) + else: + raise AttributeError, name + + def __setattr__(self, name, value): + if self.__dict__.has_key(name): + self.__dict__[name] = value + elif hasattr(self.other, name): + setattr(self.other, name, value) + else: + raise AttributeError, name + + def _getAttributeNames(self): + """Return list of magic attributes to extend introspection.""" + list = [ + 'about', + 'ask', + 'autoCallTip', + 'autoComplete', + 'autoCompleteAutoHide', + 'autoCompleteCaseInsensitive', + 'autoCompleteIncludeDouble', + 'autoCompleteIncludeMagic', + 'autoCompleteIncludeSingle', + 'callTipInsert', + 'clear', + 'pause', + 'prompt', + 'quit', + 'redirectStderr', + 'redirectStdin', + 'redirectStdout', + 'run', + 'runfile', + 'wrap', + 'zoom', + ] + list.sort() + return list + + +#DNM +DISPLAY_TEXT=""" +Author: %r +Py Version: %s +Py Shell Revision: %s +Py Interpreter Revision: %s +Python Version: %s +wxPython Version: %s +wxPython PlatformInfo: %s +Platform: %s""" + +class Shell(editwindow.EditWindow): + """Shell based on StyledTextCtrl.""" + + name = 'Shell' + revision = __revision__ + + def __init__(self, parent, id=-1, pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.CLIP_CHILDREN, + introText='', locals=None, InterpClass=None, + startupScript=None, execStartupScript=True, + *args, **kwds): + """Create Shell instance.""" + editwindow.EditWindow.__init__(self, parent, id, pos, size, style) + self.wrap() + if locals is None: + import __main__ + locals = __main__.__dict__ + + # Grab these so they can be restored by self.redirect* methods. + self.stdin = sys.stdin + self.stdout = sys.stdout + self.stderr = sys.stderr + + # Import a default interpreter class if one isn't provided. + if InterpClass == None: + from interpreter import Interpreter + else: + Interpreter = InterpClass + + # Create a replacement for stdin. + self.reader = PseudoFileIn(self.readline, self.readlines) + self.reader.input = '' + self.reader.isreading = False + + # Set up the interpreter. + self.interp = Interpreter(locals=locals, + rawin=self.raw_input, + stdin=self.reader, + stdout=PseudoFileOut(self.writeOut), + stderr=PseudoFileErr(self.writeErr), + *args, **kwds) + + # Set up the buffer. + self.buffer = Buffer() + + # Find out for which keycodes the interpreter will autocomplete. + self.autoCompleteKeys = self.interp.getAutoCompleteKeys() + + # Keep track of the last non-continuation prompt positions. + self.promptPosStart = 0 + self.promptPosEnd = 0 + + # Keep track of multi-line commands. + self.more = False + + # For use with forced updates during long-running scripts + self.lastUpdate=None + + # Create the command history. Commands are added into the + # front of the list (ie. at index 0) as they are entered. + # self.historyIndex is the current position in the history; it + # gets incremented as you retrieve the previous command, + # decremented as you retrieve the next, and reset when you hit + # Enter. self.historyIndex == -1 means you're on the current + # command, not in the history. + self.history = [] + self.historyIndex = -1 + + #seb add mode for "free edit" + self.noteMode = 0 + self.MarkerDefine(0,stc.STC_MARK_ROUNDRECT) # marker for hidden + self.searchTxt = "" + + # Assign handlers for keyboard events. + self.Bind(wx.EVT_CHAR, self.OnChar) + self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) + + # Assign handler for the context menu + self.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI) + + # Assign handlers for edit events + self.Bind(wx.EVT_MENU, lambda evt: self.Cut(), id=wx.ID_CUT) + self.Bind(wx.EVT_MENU, lambda evt: self.Copy(), id=wx.ID_COPY) + self.Bind(wx.EVT_MENU, lambda evt: self.CopyWithPrompts(), id=frame.ID_COPY_PLUS) + self.Bind(wx.EVT_MENU, lambda evt: self.Paste(), id=wx.ID_PASTE) + self.Bind(wx.EVT_MENU, lambda evt: self.PasteAndRun(), id=frame.ID_PASTE_PLUS) + self.Bind(wx.EVT_MENU, lambda evt: self.SelectAll(), id=wx.ID_SELECTALL) + self.Bind(wx.EVT_MENU, lambda evt: self.Clear(), id=wx.ID_CLEAR) + self.Bind(wx.EVT_MENU, lambda evt: self.Undo(), id=wx.ID_UNDO) + self.Bind(wx.EVT_MENU, lambda evt: self.Redo(), id=wx.ID_REDO) + + + # Assign handler for idle time. + self.waiting = False + self.Bind(wx.EVT_IDLE, self.OnIdle) + + # Display the introductory banner information. + self.showIntro(introText) + + # Assign some pseudo keywords to the interpreter's namespace. + self.setBuiltinKeywords() + + # Add 'shell' to the interpreter's local namespace. + self.setLocalShell() + + ## NOTE: See note at bottom of this file... + ## #seb: File drag and drop + ## self.SetDropTarget( FileDropTarget(self) ) + + # Do this last so the user has complete control over their + # environment. They can override anything they want. + if execStartupScript: + if startupScript is None: + startupScript = os.environ.get('PYTHONSTARTUP') + self.execStartupScript(startupScript) + else: + self.prompt() + + wx.CallAfter(self.ScrollToLine, 0) + + + def clearHistory(self): + self.history = [] + self.historyIndex = -1 + dispatcher.send(signal="Shell.clearHistory") + + + def destroy(self): + del self.interp + + def setFocus(self): + """Set focus to the shell.""" + self.SetFocus() + + def OnIdle(self, event): + """Free the CPU to do other things.""" + if self.waiting: + time.sleep(0.05) + event.Skip() + + def showIntro(self, text=''): + """Display introductory text in the shell.""" + if text: + self.write(text) + try: + if self.interp.introText: + if text and not text.endswith(os.linesep): + self.write(os.linesep) + self.write(self.interp.introText) + except AttributeError: + pass + + def setBuiltinKeywords(self): + """Create pseudo keywords as part of builtins. + + This sets "close", "exit" and "quit" to a helpful string. + """ + import __builtin__ + __builtin__.close = __builtin__.exit = __builtin__.quit = \ + 'Click on the close button to leave the application.' + __builtin__.cd = cd + __builtin__.ls = ls + __builtin__.pwd = pwd + __builtin__.sx = sx + + + def quit(self): + """Quit the application.""" + # XXX Good enough for now but later we want to send a close event. + # In the close event handler we can make sure they want to + # quit. Other applications, like PythonCard, may choose to + # hide rather than quit so we should just post the event and + # let the surrounding app decide what it wants to do. + self.write('Click on the close button to leave the application.') + + + def setLocalShell(self): + """Add 'shell' to locals as reference to ShellFacade instance.""" + self.interp.locals['shell'] = ShellFacade(other=self) + + + def execStartupScript(self, startupScript): + """Execute the user's PYTHONSTARTUP script if they have one.""" + if startupScript and os.path.isfile(startupScript): + text = 'Startup script executed: ' + startupScript + self.push('print %r; execfile(%r)' % (text, startupScript)) + self.interp.startupScript = startupScript + else: + self.push('') + + + def about(self): + """Display information about Py.""" + #DNM + text = DISPLAY_TEXT % \ + (__author__, VERSION, self.revision, self.interp.revision, + sys.version.split()[0], wx.VERSION_STRING, str(wx.PlatformInfo), + sys.platform) + self.write(text.strip()) + + + def OnChar(self, event): + """Keypress event handler. + + Only receives an event if OnKeyDown calls event.Skip() for the + corresponding event.""" + + if self.noteMode: + event.Skip() + return + + # Prevent modification of previously submitted + # commands/responses. + if not self.CanEdit(): + return + key = event.GetKeyCode() + currpos = self.GetCurrentPos() + stoppos = self.promptPosEnd + # Return (Enter) needs to be ignored in this handler. + if key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]: + pass + elif key in self.autoCompleteKeys: + # Usually the dot (period) key activates auto completion. + # Get the command between the prompt and the cursor. Add + # the autocomplete character to the end of the command. + if self.AutoCompActive(): + self.AutoCompCancel() + command = self.GetTextRange(stoppos, currpos) + chr(key) + self.write(chr(key)) + if self.autoComplete: + self.autoCompleteShow(command) + elif key == ord('('): + # The left paren activates a call tip and cancels an + # active auto completion. + if self.AutoCompActive(): + self.AutoCompCancel() + # Get the command between the prompt and the cursor. Add + # the '(' to the end of the command. + self.ReplaceSelection('') + command = self.GetTextRange(stoppos, currpos) + '(' + self.write('(') + self.autoCallTipShow(command, self.GetCurrentPos() == self.GetTextLength()) + else: + # Allow the normal event handling to take place. + event.Skip() + + + def OnKeyDown(self, event): + """Key down event handler.""" + + key = event.GetKeyCode() + # If the auto-complete window is up let it do its thing. + if self.AutoCompActive(): + event.Skip() + return + + # Prevent modification of previously submitted + # commands/responses. + controlDown = event.ControlDown() + rawControlDown = event.RawControlDown() + altDown = event.AltDown() + shiftDown = event.ShiftDown() + currpos = self.GetCurrentPos() + endpos = self.GetTextLength() + selecting = self.GetSelectionStart() != self.GetSelectionEnd() + + if (rawControlDown or controlDown) and shiftDown and key in (ord('F'), ord('f')): + li = self.GetCurrentLine() + m = self.MarkerGet(li) + if m & 1<<0: + startP = self.PositionFromLine(li) + self.MarkerDelete(li, 0) + maxli = self.GetLineCount() + li += 1 # li stayed visible as header-line + li0 = li + while li<maxli and self.GetLineVisible(li) == 0: + li += 1 + endP = self.GetLineEndPosition(li-1) + self.ShowLines(li0, li-1) + # select reappearing text to allow "hide again" + self.SetSelection( startP, endP ) + return + startP,endP = self.GetSelection() + endP-=1 + startL = self.LineFromPosition(startP) + endL = self.LineFromPosition(endP) + + # never hide last prompt + if endL == self.LineFromPosition(self.promptPosEnd): + endL -= 1 + + m = self.MarkerGet(startL) + self.MarkerAdd(startL, 0) + self.HideLines(startL+1,endL) + self.SetCurrentPos( startP ) # to ensure caret stays visible ! + + if key == wx.WXK_F12: #seb + if self.noteMode: + # self.promptPosStart not used anyway - or ? + self.promptPosEnd = \ + self.PositionFromLine( self.GetLineCount()-1 ) + \ + len(str(sys.ps1)) + self.GotoLine(self.GetLineCount()) + self.GotoPos(self.promptPosEnd) + self.prompt() #make sure we have a prompt + self.SetCaretForeground("black") + self.SetCaretWidth(1) #default + self.SetCaretPeriod(500) #default + else: + self.SetCaretForeground("red") + self.SetCaretWidth(4) + self.SetCaretPeriod(0) #steady + + self.noteMode = not self.noteMode + return + if self.noteMode: + event.Skip() + return + + # Return (Enter) is used to submit a command to the + # interpreter. + if (not (rawControlDown or controlDown) and not shiftDown and not altDown) and \ + key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]: + if self.CallTipActive(): + self.CallTipCancel() + self.processLine() + + # Complete Text (from already typed words) + elif shiftDown and key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]: + self.OnShowCompHistory() + + # Ctrl+Return (Ctrl+Enter) is used to insert a line break. + elif (rawControlDown or controlDown) and key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]: + if self.CallTipActive(): + self.CallTipCancel() + if currpos == endpos: + self.processLine() + else: + self.insertLineBreak() + + # Let Ctrl-Alt-* get handled normally. + elif (rawControlDown or controlDown) and altDown: + event.Skip() + + # Clear the current, unexecuted command. + elif key == wx.WXK_ESCAPE: + if self.CallTipActive(): + event.Skip() + else: + self.clearCommand() + + # Clear the current command + elif key == wx.WXK_BACK and (rawControlDown or controlDown) and shiftDown: + self.clearCommand() + + # Increase font size. + elif (rawControlDown or controlDown) and key in (ord(']'), wx.WXK_NUMPAD_ADD): + dispatcher.send(signal='FontIncrease') + + # Decrease font size. + elif (rawControlDown or controlDown) and key in (ord('['), wx.WXK_NUMPAD_SUBTRACT): + dispatcher.send(signal='FontDecrease') + + # Default font size. + elif (rawControlDown or controlDown) and key in (ord('='), wx.WXK_NUMPAD_DIVIDE): + dispatcher.send(signal='FontDefault') + + # Cut to the clipboard. + elif ((rawControlDown or controlDown) and key in (ord('X'), ord('x'))) \ + or (shiftDown and key == wx.WXK_DELETE): + self.Cut() + + # Copy to the clipboard. + elif (rawControlDown or controlDown) and not shiftDown \ + and key in (ord('C'), ord('c'), wx.WXK_INSERT): + self.Copy() + + # Copy to the clipboard, including prompts. + elif (rawControlDown or controlDown) and shiftDown \ + and key in (ord('C'), ord('c'), wx.WXK_INSERT): + self.CopyWithPrompts() + + # Copy to the clipboard, including prefixed prompts. + elif altDown and not controlDown \ + and key in (ord('C'), ord('c'), wx.WXK_INSERT): + self.CopyWithPromptsPrefixed() + + # Home needs to be aware of the prompt. + elif (rawControlDown or controlDown) and key == wx.WXK_HOME: + home = self.promptPosEnd + if currpos > home: + self.SetCurrentPos(home) + if not selecting and not shiftDown: + self.SetAnchor(home) + self.EnsureCaretVisible() + else: + event.Skip() + + # Home needs to be aware of the prompt. + elif key == wx.WXK_HOME: + home = self.promptPosEnd + if currpos > home: + [line_str,line_len] = self.GetCurLine() + pos=self.GetCurrentPos() + if line_str[:4] in [sys.ps1,sys.ps2,sys.ps3]: + self.SetCurrentPos(pos+4-line_len) + #self.SetCurrentPos(home) + if not selecting and not shiftDown: + self.SetAnchor(pos+4-line_len) + self.EnsureCaretVisible() + else: + event.Skip() + else: + event.Skip() + + # + # The following handlers modify text, so we need to see if + # there is a selection that includes text prior to the prompt. + # + # Don't modify a selection with text prior to the prompt. + elif selecting and key not in NAVKEYS and not self.CanEdit(): + pass + + # Paste from the clipboard. + elif ((rawControlDown or controlDown) and not shiftDown and key in (ord('V'), ord('v'))) \ + or (shiftDown and not controlDown and key == wx.WXK_INSERT): + self.Paste() + + # manually invoke AutoComplete and Calltips + elif (rawControlDown or controlDown) and key == wx.WXK_SPACE: + self.OnCallTipAutoCompleteManually(shiftDown) + + # Paste from the clipboard, run commands. + elif (rawControlDown or controlDown) and shiftDown and key in (ord('V'), ord('v')): + self.PasteAndRun() + + # Replace with the previous command from the history buffer. + elif ((rawControlDown or controlDown) and not shiftDown and key == wx.WXK_UP) \ + or (altDown and key in (ord('P'), ord('p'))): + self.OnHistoryReplace(step=+1) + + # Replace with the next command from the history buffer. + elif ((rawControlDown or controlDown) and not shiftDown and key == wx.WXK_DOWN) \ + or (altDown and key in (ord('N'), ord('n'))): + self.OnHistoryReplace(step=-1) + + # Insert the previous command from the history buffer. + elif ((rawControlDown or controlDown) and shiftDown and key == wx.WXK_UP) and self.CanEdit(): + self.OnHistoryInsert(step=+1) + + # Insert the next command from the history buffer. + elif ((rawControlDown or controlDown) and shiftDown and key == wx.WXK_DOWN) and self.CanEdit(): + self.OnHistoryInsert(step=-1) + + # Search up the history for the text in front of the cursor. + elif key == wx.WXK_F8: + self.OnHistorySearch() + + # Don't backspace over the latest non-continuation prompt. + elif key == wx.WXK_BACK: + if selecting and self.CanEdit(): + event.Skip() + elif currpos > self.promptPosEnd: + event.Skip() + + # Only allow these keys after the latest prompt. + elif key in (wx.WXK_TAB, wx.WXK_DELETE): + if self.CanEdit(): + event.Skip() + + # Don't toggle between insert mode and overwrite mode. + elif key == wx.WXK_INSERT: + pass + + # Don't allow line deletion. + elif controlDown and key in (ord('L'), ord('l')): + # TODO : Allow line deletion eventually... + #event.Skip() + pass + + # Don't allow line transposition. + elif controlDown and key in (ord('T'), ord('t')): + # TODO : Allow line transposition eventually... + # TODO : Will have to adjust markers accordingly and test if allowed... + #event.Skip() + pass + + # Basic navigation keys should work anywhere. + elif key in NAVKEYS: + event.Skip() + + # Protect the readonly portion of the shell. + elif not self.CanEdit(): + pass + + else: + event.Skip() + + + def OnShowCompHistory(self): + """Show possible autocompletion Words from already typed words.""" + + #copy from history + his = self.history[:] + + #put together in one string + joined = " ".join (his) + import re + + #sort out only "good" words + newlist = re.split("[ \.\[\]=}(\)\,0-9\"]", joined) + + #length > 1 (mix out "trash") + thlist = [] + for i in newlist: + if len (i) > 1: + thlist.append (i) + + #unique (no duplicate words + #oneliner from german python forum => unique list + unlist = [thlist[i] for i in xrange(len(thlist)) if thlist[i] not in thlist[:i]] + + #sort lowercase + unlist.sort(lambda a, b: cmp(a.lower(), b.lower())) + + #this is more convenient, isn't it? + self.AutoCompSetIgnoreCase(True) + + #join again together in a string + stringlist = " ".join(unlist) + + #pos von 0 noch ausrechnen + + #how big is the offset? + cpos = self.GetCurrentPos() - 1 + while chr (self.GetCharAt (cpos)).isalnum(): + cpos -= 1 + + #the most important part + self.AutoCompShow(self.GetCurrentPos() - cpos -1, stringlist) + + + def clearCommand(self): + """Delete the current, unexecuted command.""" + startpos = self.promptPosEnd + endpos = self.GetTextLength() + self.SetSelection(startpos, endpos) + self.ReplaceSelection('') + self.more = False + + def OnHistoryReplace(self, step): + """Replace with the previous/next command from the history buffer.""" + self.clearCommand() + self.replaceFromHistory(step) + + def replaceFromHistory(self, step): + """Replace selection with command from the history buffer.""" + ps2 = str(sys.ps2) + self.ReplaceSelection('') + newindex = self.historyIndex + step + if -1 <= newindex <= len(self.history): + self.historyIndex = newindex + if 0 <= newindex <= len(self.history)-1: + command = self.history[self.historyIndex] + command = command.replace('\n', os.linesep + ps2) + self.ReplaceSelection(command) + + def OnHistoryInsert(self, step): + """Insert the previous/next command from the history buffer.""" + if not self.CanEdit(): + return + startpos = self.GetCurrentPos() + self.replaceFromHistory(step) + endpos = self.GetCurrentPos() + self.SetSelection(endpos, startpos) + + def OnHistorySearch(self): + """Search up the history buffer for the text in front of the cursor.""" + if not self.CanEdit(): + return + startpos = self.GetCurrentPos() + # The text up to the cursor is what we search for. + numCharsAfterCursor = self.GetTextLength() - startpos + searchText = self.getCommand(rstrip=False) + if numCharsAfterCursor > 0: + searchText = searchText[:-numCharsAfterCursor] + if not searchText: + return + # Search upwards from the current history position and loop + # back to the beginning if we don't find anything. + if (self.historyIndex <= -1) \ + or (self.historyIndex >= len(self.history)-2): + searchOrder = range(len(self.history)) + else: + searchOrder = range(self.historyIndex+1, len(self.history)) + \ + range(self.historyIndex) + for i in searchOrder: + command = self.history[i] + if command[:len(searchText)] == searchText: + # Replace the current selection with the one we found. + self.ReplaceSelection(command[len(searchText):]) + endpos = self.GetCurrentPos() + self.SetSelection(endpos, startpos) + # We've now warped into middle of the history. + self.historyIndex = i + break + + def setStatusText(self, text): + """Display status information.""" + + # This method will likely be replaced by the enclosing app to + # do something more interesting, like write to a status bar. + print text + + def insertLineBreak(self): + """Insert a new line break.""" + if self.CanEdit(): + self.write(os.linesep) + self.more = True + self.prompt() + + def processLine(self): + """Process the line of text at which the user hit Enter.""" + + # The user hit ENTER and we need to decide what to do. They + # could be sitting on any line in the shell. + + thepos = self.GetCurrentPos() + startpos = self.promptPosEnd + endpos = self.GetTextLength() + ps2 = str(sys.ps2) + # If they hit RETURN inside the current command, execute the + # command. + if self.CanEdit(): + self.SetCurrentPos(endpos) + self.interp.more = False + command = self.GetTextRange(startpos, endpos) + lines = command.split(os.linesep + ps2) + lines = [line.rstrip() for line in lines] + command = '\n'.join(lines) + if self.reader.isreading: + if not command: + # Match the behavior of the standard Python shell + # when the user hits return without entering a + # value. + command = '\n' + self.reader.input = command + self.write(os.linesep) + else: + self.push(command) + wx.FutureCall(1, self.EnsureCaretVisible) + # Or replace the current command with the other command. + else: + # If the line contains a command (even an invalid one). + if self.getCommand(rstrip=False): + command = self.getMultilineCommand() + self.clearCommand() + self.write(command) + # Otherwise, put the cursor back where we started. + else: + self.SetCurrentPos(thepos) + self.SetAnchor(thepos) + + def getMultilineCommand(self, rstrip=True): + """Extract a multi-line command from the editor. + + The command may not necessarily be valid Python syntax.""" + # XXX Need to extract real prompts here. Need to keep track of + # the prompt every time a command is issued. + ps1 = str(sys.ps1) + ps1size = len(ps1) + ps2 = str(sys.ps2) + ps2size = len(ps2) + # This is a total hack job, but it works. + text = self.GetCurLine()[0] + line = self.GetCurrentLine() + while text[:ps2size] == ps2 and line > 0: + line -= 1 + self.GotoLine(line) + text = self.GetCurLine()[0] + if text[:ps1size] == ps1: + line = self.GetCurrentLine() + self.GotoLine(line) + startpos = self.GetCurrentPos() + ps1size + line += 1 + self.GotoLine(line) + while self.GetCurLine()[0][:ps2size] == ps2: + line += 1 + self.GotoLine(line) + stoppos = self.GetCurrentPos() + command = self.GetTextRange(startpos, stoppos) + command = command.replace(os.linesep + ps2, '\n') + command = command.rstrip() + command = command.replace('\n', os.linesep + ps2) + else: + command = '' + if rstrip: + command = command.rstrip() + return command + + def getCommand(self, text=None, rstrip=True): + """Extract a command from text which may include a shell prompt. + + The command may not necessarily be valid Python syntax.""" + if not text: + text = self.GetCurLine()[0] + # Strip the prompt off the front leaving just the command. + command = self.lstripPrompt(text) + if command == text: + command = '' # Real commands have prompts. + if rstrip: + command = command.rstrip() + return command + + def lstripPrompt(self, text): + """Return text without a leading prompt.""" + ps1 = str(sys.ps1) + ps1size = len(ps1) + ps2 = str(sys.ps2) + ps2size = len(ps2) + # Strip the prompt off the front of text. + if text[:ps1size] == ps1: + text = text[ps1size:] + elif text[:ps2size] == ps2: + text = text[ps2size:] + return text + + def push(self, command, silent = False): + """Send command to the interpreter for execution.""" + if not silent: + self.write(os.linesep) + + #DNM + if USE_MAGIC: + command=magic(command) + + busy = wx.BusyCursor() + self.waiting = True + self.lastUpdate=None + self.more = self.interp.push(command) + self.lastUpdate=None + self.waiting = False + del busy + if not self.more: + self.addHistory(command.rstrip()) + if not silent: + self.prompt() + + def addHistory(self, command): + """Add command to the command history.""" + # Reset the history position. + self.historyIndex = -1 + # Insert this command into the history, unless it's a blank + # line or the same as the last command. + if command != '' \ + and (len(self.history) == 0 or command != self.history[0]): + self.history.insert(0, command) + dispatcher.send(signal="Shell.addHistory", command=command) + + def write(self, text): + """Display text in the shell. + + Replace line endings with OS-specific endings.""" + text = self.fixLineEndings(text) + self.AddText(text) + self.EnsureCaretVisible() + + if self.waiting: + if self.lastUpdate==None: + self.lastUpdate=time.time() + if time.time()-self.lastUpdate > PRINT_UPDATE_MAX_TIME: + self.Update() + self.lastUpdate=time.time() + + def fixLineEndings(self, text): + """Return text with line endings replaced by OS-specific endings.""" + lines = text.split('\r\n') + for l in range(len(lines)): + chunks = lines[l].split('\r') + for c in range(len(chunks)): + chunks[c] = os.linesep.join(chunks[c].split('\n')) + lines[l] = os.linesep.join(chunks) + text = os.linesep.join(lines) + return text + + def prompt(self): + """Display proper prompt for the context: ps1, ps2 or ps3. + + If this is a continuation line, autoindent as necessary.""" + isreading = self.reader.isreading + skip = False + if isreading: + prompt = str(sys.ps3) + elif self.more: + prompt = str(sys.ps2) + else: + prompt = str(sys.ps1) + pos = self.GetCurLine()[1] + if pos > 0: + if isreading: + skip = True + else: + self.write(os.linesep) + if not self.more: + self.promptPosStart = self.GetCurrentPos() + if not skip: + self.write(prompt) + if not self.more: + self.promptPosEnd = self.GetCurrentPos() + # Keep the undo feature from undoing previous responses. + self.EmptyUndoBuffer() + + if self.more: + line_num=self.GetCurrentLine() + currentLine=self.GetLine(line_num) + previousLine=self.GetLine(line_num-1)[len(prompt):] + pstrip=previousLine.strip() + lstrip=previousLine.lstrip() + + # Get the first alnum word: + first_word=[] + for i in pstrip: + if i.isalnum(): + first_word.append(i) + else: + break + first_word = ''.join(first_word) + + if pstrip == '': + # because it is all whitespace! + indent=previousLine.strip('\n').strip('\r') + else: + indent=previousLine[:(len(previousLine)-len(lstrip))] + if pstrip[-1]==':' and \ + first_word in ['if','else','elif','for','while', + 'def','class','try','except','finally']: + indent+=' '*4 + + self.write(indent) + self.EnsureCaretVisible() + self.ScrollToColumn(0) + + def readline(self): + """Replacement for stdin.readline().""" + input = '' + reader = self.reader + reader.isreading = True + self.prompt() + try: + while not reader.input: + wx.YieldIfNeeded() + input = reader.input + finally: + reader.input = '' + reader.isreading = False + input = str(input) # In case of Unicode. + return input + + def readlines(self): + """Replacement for stdin.readlines().""" + lines = [] + while lines[-1:] != ['\n']: + lines.append(self.readline()) + return lines + + def raw_input(self, prompt=''): + """Return string based on user input.""" + if prompt: + self.write(prompt) + return self.readline() + + def ask(self, prompt='Please enter your response:'): + """Get response from the user using a dialog box.""" + dialog = wx.TextEntryDialog(None, prompt, + 'Input Dialog (Raw)', '') + try: + if dialog.ShowModal() == wx.ID_OK: + text = dialog.GetValue() + return text + finally: + dialog.Destroy() + return '' + + def pause(self): + """Halt execution pending a response from the user.""" + self.ask('Press enter to continue:') + + def clear(self): + """Delete all text from the shell.""" + self.ClearAll() + + def run(self, command, prompt=True, verbose=True): + """Execute command as if it was typed in directly. + >>> shell.run('print "this"') + >>> print "this" + this + >>> + """ + # Go to the very bottom of the text. + endpos = self.GetTextLength() + self.SetCurrentPos(endpos) + command = command.rstrip() + if prompt: self.prompt() + if verbose: self.write(command) + self.push(command) + + def runfile(self, filename): + """Execute all commands in file as if they were typed into the + shell.""" + file = open(filename) + try: + self.prompt() + for command in file.readlines(): + if command[:6] == 'shell.': + # Run shell methods silently. + self.run(command, prompt=False, verbose=False) + else: + self.run(command, prompt=False, verbose=True) + finally: + file.close() + + def autoCompleteShow(self, command, offset = 0): + """Display auto-completion popup list.""" + self.AutoCompSetAutoHide(self.autoCompleteAutoHide) + self.AutoCompSetIgnoreCase(self.autoCompleteCaseInsensitive) + list = self.interp.getAutoCompleteList(command, + includeMagic=self.autoCompleteIncludeMagic, + includeSingle=self.autoCompleteIncludeSingle, + includeDouble=self.autoCompleteIncludeDouble) + if list: + options = ' '.join(list) + #offset = 0 + self.AutoCompShow(offset, options) + + def autoCallTipShow(self, command, insertcalltip = True, forceCallTip = False): + """Display argument spec and docstring in a popup window.""" + if self.CallTipActive(): + self.CallTipCancel() + (name, argspec, tip) = self.interp.getCallTip(command) + if tip: + dispatcher.send(signal='Shell.calltip', sender=self, calltip=tip) + if not self.autoCallTip and not forceCallTip: + return + startpos = self.GetCurrentPos() + if argspec and insertcalltip and self.callTipInsert: + self.write(argspec + ')') + endpos = self.GetCurrentPos() + self.SetSelection(startpos, endpos) + if tip: + tippos = startpos - (len(name) + 1) + fallback = startpos - self.GetColumn(startpos) + # In case there isn't enough room, only go back to the + # fallback. + tippos = max(tippos, fallback) + self.CallTipShow(tippos, tip) + + def OnCallTipAutoCompleteManually (self, shiftDown): + """AutoComplete and Calltips manually.""" + if self.AutoCompActive(): + self.AutoCompCancel() + currpos = self.GetCurrentPos() + stoppos = self.promptPosEnd + + cpos = currpos + #go back until '.' is found + pointavailpos = -1 + while cpos >= stoppos: + if self.GetCharAt(cpos) == ord ('.'): + pointavailpos = cpos + break + cpos -= 1 + + #word from non whitespace until '.' + if pointavailpos != -1: + #look backward for first whitespace char + textbehind = self.GetTextRange (pointavailpos + 1, currpos) + pointavailpos += 1 + + if not shiftDown: + #call AutoComplete + stoppos = self.promptPosEnd + textbefore = self.GetTextRange(stoppos, pointavailpos) + self.autoCompleteShow(textbefore, len (textbehind)) + else: + #call CallTips + cpos = pointavailpos + begpos = -1 + while cpos > stoppos: + if chr(self.GetCharAt(cpos)).isspace(): + begpos = cpos + break + cpos -= 1 + if begpos == -1: + begpos = cpos + ctips = self.GetTextRange (begpos, currpos) + ctindex = ctips.find ('(') + if ctindex != -1 and not self.CallTipActive(): + #insert calltip, if current pos is '(', otherwise show it only + self.autoCallTipShow(ctips[:ctindex + 1], + self.GetCharAt(currpos - 1) == ord('(') and \ + self.GetCurrentPos() == self.GetTextLength(), + True) + + + def writeOut(self, text): + """Replacement for stdout.""" + self.write(text) + + def writeErr(self, text): + """Replacement for stderr.""" + self.write(text) + + def redirectStdin(self, redirect=True): + """If redirect is true then sys.stdin will come from the shell.""" + if redirect: + sys.stdin = self.reader + else: + sys.stdin = self.stdin + + def redirectStdout(self, redirect=True): + """If redirect is true then sys.stdout will go to the shell.""" + if redirect: + sys.stdout = PseudoFileOut(self.writeOut) + else: + sys.stdout = self.stdout + + def redirectStderr(self, redirect=True): + """If redirect is true then sys.stderr will go to the shell.""" + if redirect: + sys.stderr = PseudoFileErr(self.writeErr) + else: + sys.stderr = self.stderr + + def CanCut(self): + """Return true if text is selected and can be cut.""" + if self.GetSelectionStart() != self.GetSelectionEnd() \ + and self.GetSelectionStart() >= self.promptPosEnd \ + and self.GetSelectionEnd() >= self.promptPosEnd: + return True + else: + return False + + def CanPaste(self): + """Return true if a paste should succeed.""" + if self.CanEdit() and editwindow.EditWindow.CanPaste(self): + return True + else: + return False + + def CanEdit(self): + """Return true if editing should succeed.""" + if self.GetSelectionStart() != self.GetSelectionEnd(): + if self.GetSelectionStart() >= self.promptPosEnd \ + and self.GetSelectionEnd() >= self.promptPosEnd: + return True + else: + return False + else: + return self.GetCurrentPos() >= self.promptPosEnd + + def Cut(self): + """Remove selection and place it on the clipboard.""" + if self.CanCut() and self.CanCopy(): + if self.AutoCompActive(): + self.AutoCompCancel() + if self.CallTipActive(): + self.CallTipCancel() + self.Copy() + self.ReplaceSelection('') + + def Copy(self): + """Copy selection and place it on the clipboard.""" + if self.CanCopy(): + ps1 = str(sys.ps1) + ps2 = str(sys.ps2) + command = self.GetSelectedText() + command = command.replace(os.linesep + ps2, os.linesep) + command = command.replace(os.linesep + ps1, os.linesep) + command = self.lstripPrompt(text=command) + data = wx.TextDataObject(command) + self._clip(data) + + def CopyWithPrompts(self): + """Copy selection, including prompts, and place it on the clipboard.""" + if self.CanCopy(): + command = self.GetSelectedText() + data = wx.TextDataObject(command) + self._clip(data) + + def CopyWithPromptsPrefixed(self): + """Copy selection, including prompts prefixed with four + spaces, and place it on the clipboard.""" + if self.CanCopy(): + command = self.GetSelectedText() + spaces = ' ' * 4 + command = spaces + command.replace(os.linesep, + os.linesep + spaces) + data = wx.TextDataObject(command) + self._clip(data) + + def _clip(self, data): + if wx.TheClipboard.Open(): + wx.TheClipboard.UsePrimarySelection(False) + wx.TheClipboard.SetData(data) + wx.TheClipboard.Flush() + wx.TheClipboard.Close() + + def Paste(self): + """Replace selection with clipboard contents.""" + if self.CanPaste() and wx.TheClipboard.Open(): + ps2 = str(sys.ps2) + if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)): + data = wx.TextDataObject() + if wx.TheClipboard.GetData(data): + self.ReplaceSelection('') + command = data.GetText() + command = command.rstrip() + command = self.fixLineEndings(command) + command = self.lstripPrompt(text=command) + command = command.replace(os.linesep + ps2, '\n') + command = command.replace(os.linesep, '\n') + command = command.replace('\n', os.linesep + ps2) + self.write(command) + wx.TheClipboard.Close() + + + def PasteAndRun(self): + """Replace selection with clipboard contents, run commands.""" + text = '' + if wx.TheClipboard.Open(): + if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)): + data = wx.TextDataObject() + if wx.TheClipboard.GetData(data): + text = data.GetText() + wx.TheClipboard.Close() + if text: + self.Execute(text) + + + def Execute(self, text): + """Replace selection with text and run commands.""" + ps1 = str(sys.ps1) + ps2 = str(sys.ps2) + endpos = self.GetTextLength() + self.SetCurrentPos(endpos) + startpos = self.promptPosEnd + self.SetSelection(startpos, endpos) + self.ReplaceSelection('') + text = text.lstrip() + text = self.fixLineEndings(text) + text = self.lstripPrompt(text) + text = text.replace(os.linesep + ps1, '\n') + text = text.replace(os.linesep + ps2, '\n') + text = text.replace(os.linesep, '\n') + lines = text.split('\n') + commands = [] + command = '' + for line in lines: + if line.strip() == ps2.strip(): + # If we are pasting from something like a + # web page that drops the trailing space + # from the ps2 prompt of a blank line. + line = '' + lstrip = line.lstrip() + if line.strip() != '' and lstrip == line and \ + lstrip[:4] not in ['else','elif'] and \ + lstrip[:6] != 'except': + # New command. + if command: + # Add the previous command to the list. + commands.append(command) + # Start a new command, which may be multiline. + command = line + else: + # Multiline command. Add to the command. + command += '\n' + command += line + commands.append(command) + for command in commands: + command = command.replace('\n', os.linesep + ps2) + self.write(command) + self.processLine() + + + def wrap(self, wrap=True): + """Sets whether text is word wrapped.""" + try: + self.SetWrapMode(wrap) + except AttributeError: + return 'Wrapping is not available in this version.' + + def zoom(self, points=0): + """Set the zoom level. + + This number of points is added to the size of all fonts. It + may be positive to magnify or negative to reduce.""" + self.SetZoom(points) + + + + def LoadSettings(self, config): + self.autoComplete = \ + config.ReadBool('Options/AutoComplete', True) + self.autoCompleteIncludeMagic = \ + config.ReadBool('Options/AutoCompleteIncludeMagic', True) + self.autoCompleteIncludeSingle = \ + config.ReadBool('Options/AutoCompleteIncludeSingle', True) + self.autoCompleteIncludeDouble = \ + config.ReadBool('Options/AutoCompleteIncludeDouble', True) + + self.autoCallTip = config.ReadBool('Options/AutoCallTip', True) + self.callTipInsert = config.ReadBool('Options/CallTipInsert', True) + self.SetWrapMode(config.ReadBool('View/WrapMode', True)) + + self.lineNumbers = config.ReadBool('View/ShowLineNumbers', True) + self.setDisplayLineNumbers (self.lineNumbers) + zoom = config.ReadInt('View/Zoom/Shell', -99) + if zoom != -99: + self.SetZoom(zoom) + + + def SaveSettings(self, config): + config.WriteBool('Options/AutoComplete', self.autoComplete) + config.WriteBool('Options/AutoCompleteIncludeMagic', + self.autoCompleteIncludeMagic) + config.WriteBool('Options/AutoCompleteIncludeSingle', + self.autoCompleteIncludeSingle) + config.WriteBool('Options/AutoCompleteIncludeDouble', + self.autoCompleteIncludeDouble) + config.WriteBool('Options/AutoCallTip', self.autoCallTip) + config.WriteBool('Options/CallTipInsert', self.callTipInsert) + config.WriteBool('View/WrapMode', self.GetWrapMode()) + config.WriteBool('View/ShowLineNumbers', self.lineNumbers) + config.WriteInt('View/Zoom/Shell', self.GetZoom()) + + def GetContextMenu(self): + """ + Create and return a context menu for the shell. + This is used instead of the scintilla default menu + in order to correctly respect our immutable buffer. + """ + menu = wx.Menu() + menu.Append(wx.ID_UNDO, "Undo") + menu.Append(wx.ID_REDO, "Redo") + + menu.AppendSeparator() + + menu.Append(wx.ID_CUT, "Cut") + menu.Append(wx.ID_COPY, "Copy") + menu.Append(frame.ID_COPY_PLUS, "Copy Plus") + menu.Append(wx.ID_PASTE, "Paste") + menu.Append(frame.ID_PASTE_PLUS, "Paste Plus") + menu.Append(wx.ID_CLEAR, "Clear") + + menu.AppendSeparator() + + menu.Append(wx.ID_SELECTALL, "Select All") + return menu + + def OnContextMenu(self, evt): + menu = self.GetContextMenu() + self.PopupMenu(menu) + + def OnUpdateUI(self, evt): + id = evt.Id + if id in (wx.ID_CUT, wx.ID_CLEAR): + evt.Enable(self.CanCut()) + elif id in (wx.ID_COPY, frame.ID_COPY_PLUS): + evt.Enable(self.CanCopy()) + elif id in (wx.ID_PASTE, frame.ID_PASTE_PLUS): + evt.Enable(self.CanPaste()) + elif id == wx.ID_UNDO: + evt.Enable(self.CanUndo()) + elif id == wx.ID_REDO: + evt.Enable(self.CanRedo()) + + + + +## NOTE: The DnD of file names is disabled until we can figure out how +## best to still allow DnD of text. + + +## #seb : File drag and drop +## class FileDropTarget(wx.FileDropTarget): +## def __init__(self, obj): +## wx.FileDropTarget.__init__(self) +## self.obj = obj +## def OnDropFiles(self, x, y, filenames): +## if len(filenames) == 1: +## txt = 'r\"%s\"' % filenames[0] +## else: +## txt = '( ' +## for f in filenames: +## txt += 'r\"%s\" , ' % f +## txt += ')' +## self.obj.AppendText(txt) +## pos = self.obj.GetCurrentPos() +## self.obj.SetCurrentPos( pos ) +## self.obj.SetSelection( pos, pos ) + + + +## class TextAndFileDropTarget(wx.DropTarget): +## def __init__(self, shell): +## wx.DropTarget.__init__(self) +## self.shell = shell +## self.compdo = wx.DataObjectComposite() +## self.textdo = wx.TextDataObject() +## self.filedo = wx.FileDataObject() +## self.compdo.Add(self.textdo) +## self.compdo.Add(self.filedo, True) + +## self.SetDataObject(self.compdo) + +## def OnDrop(self, x, y): +## return True + +## def OnData(self, x, y, result): +## self.GetData() +## if self.textdo.GetTextLength() > 1: +## text = self.textdo.GetText() +## # *** Do somethign with the dragged text here... +## self.textdo.SetText('') +## else: +## filenames = str(self.filename.GetFilenames()) +## if len(filenames) == 1: +## txt = 'r\"%s\"' % filenames[0] +## else: +## txt = '( ' +## for f in filenames: +## txt += 'r\"%s\" , ' % f +## txt += ')' +## self.shell.AppendText(txt) +## pos = self.shell.GetCurrentPos() +## self.shell.SetCurrentPos( pos ) +## self.shell.SetSelection( pos, pos ) + +## return result diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/sliceshell.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/sliceshell.py new file mode 100644 index 0000000..437fce7 --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/sliceshell.py @@ -0,0 +1,3789 @@ +"""Slices is an interactive text control in which a user types in +commands to be sent to the interpreter. This particular shell is +based on wxPython's wxStyledTextCtrl. + +Sponsored by Orbtech - Your source for Python programming expertise. +Slices is a version of shell modified by David Mashburn.""" + +__author__ = "David N. Mashburn <david.n.mashburn@gmail.com> / " +__author__ += "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id: sliceshell.py 60100 2009-04-12 02:56:29Z RD $" +__revision__ = "$Revision: 60100 $"[11:-2] + +import wx +from wx import stc + +import keyword +import os +import sys +import time + +from buffer import Buffer +import dispatcher +import editor +import editwindow +import document +import frame +from pseudo import PseudoFileIn +from pseudo import PseudoFileOut +from pseudo import PseudoFileErr +from version import VERSION +from magic import magic +from parse import testForContinuations +from path import ls,cd,pwd,sx + + +sys.ps3 = '<-- ' # Input prompt. +USE_MAGIC=True +# Force updates from long-running commands after this many seconds +PRINT_UPDATE_MAX_TIME=2 + +NAVKEYS = (wx.WXK_HOME, wx.WXK_END, wx.WXK_LEFT, wx.WXK_RIGHT, + wx.WXK_UP, wx.WXK_DOWN, wx.WXK_PRIOR, wx.WXK_NEXT) + +GROUPING_SELECTING=0 +IO_SELECTING = 1 + +GROUPING_START = 2 +GROUPING_START_FOLDED = 3 +GROUPING_MIDDLE = 4 +GROUPING_END = 5 +INPUT_START = 6 +INPUT_START_FOLDED = 7 +INPUT_MIDDLE = 8 +INPUT_END = 9 +OUTPUT_START = 10 +OUTPUT_START_FOLDED = 11 +OUTPUT_MIDDLE = 12 +OUTPUT_END = 13 + +OUTPUT_BG = 14 +READLINE_BG = 15 +INPUT_READLINE = 16 + +# Could add C integration right into the markers... +# Non-editable file marker for auto-loaded files... +# Weave VariableInput = 15 +# Weave C code = 16 +# C code = 17 (only for use with Pyrex) +# Pyrex / Cython code = 18 + +GROUPING_MASK = ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED | + 1<<GROUPING_MIDDLE | 1<<GROUPING_END ) + +INPUT_MASK = ( 1<<INPUT_START | 1<<INPUT_START_FOLDED | + 1<<INPUT_MIDDLE | 1<<INPUT_END ) +OUTPUT_MASK = ( 1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED | + 1<<OUTPUT_MIDDLE | 1<<OUTPUT_END ) +IO_MASK = ( INPUT_MASK | OUTPUT_MASK ) + +IO_START_MASK = ( 1<<INPUT_START | 1<<OUTPUT_START ) +IO_START_FOLDED_MASK = ( 1<<INPUT_START_FOLDED | 1<<OUTPUT_START_FOLDED ) +IO_ANY_START_MASK = ( 1<<INPUT_START | 1<<OUTPUT_START | + 1<<INPUT_START_FOLDED | 1<<OUTPUT_START_FOLDED ) +IO_MIDDLE_MASK = ( 1<<INPUT_MIDDLE | 1<<OUTPUT_MIDDLE ) +IO_END_MASK = ( 1<<INPUT_END | 1<<OUTPUT_END ) + +usrBinEnvPythonText = '#!/usr/bin/env python2\n' +pyslicesFormatHeaderText = ['#PySlices Save Format Version 1.1 (PySlices v0.9.7.8 and later)\n', + '#PySlices Save Format Version 1.2 (PySlices v0.9.8 and later)\n'] +groupingStartText = '#PySlices Marker Information -- Begin Grouping Slice\n' +inputStartText = '#PySlices Marker Information -- Begin Input Slice\n' +outputStartText = '#PySlices Marker Information -- Begin Output Slice\n' + +tutorialText = """ + + Tutorial!!! +------------------------------------------------------------------------ +PySlices is the newest member of the Py suite! +It is a modified version of PyCrust that supports multi-line commands. + +Input and output are contained in "Slices" shown as markers in the left margin. +Input Slices have RED margins (active, editable). +Output Slices have BLUE margins (frozen, not editable). + +Commands in slices can be on more than one line, as with Sage or Mathematica. +For example, the command: +a=1 +b=2 +print a+b +will all run in sequence, much like a script. +Try running the above Input Slice by clicking somewhere in its text and +using Ctrl-Return, Shift-Return, or Numpad Enter to execute. +Previous commands (Old Slices) can be re-edited and run again in place. + +Slices can also be: + * selceted (click on the margin, Shift-click for multiple selection) + * folded (click the margin twice) + * selected and deleted (hit delete while selected) + * divided (Ctrl-D) + * merged (Ctrl-M while selecting adjacent, like-colored slices) + +Try deleting the slice above this one by clicking on the red margin. + +If you want a more traditional shell feel, try enabling "Shell Mode" in +"Options->Settings->Shell Mode" (or try PyCrust). +In Shell Mode, two returns in a row executes the command, and + Ctrl-Return and Shift-Return always print newlines. + +Saving and opening "sessions" is now supported! This is a little +different to other shells where the history is saved. With PySlices, +the whole document is saved in a simple text format! + +To disable this Tutorial on startup, uncheck it in the menu at: +"Options->Startup->Show PySlices tutorial" + +PySlices may not be the best thing since sliced bread, but +I hope it makes using Python a little bit sweeter! +""" + +class SlicesShellFrame(frame.Frame, frame.ShellFrameMixin): + """Frame containing the sliceshell component.""" + + name = 'SlicesShell Frame' + revision = __revision__ + + def __init__(self, parent=None, id=-1, title='PySlicesShell', + pos=wx.DefaultPosition, size=wx.DefaultSize, + style=wx.DEFAULT_FRAME_STYLE, locals=None, + InterpClass=None, + config=None, dataDir=None, filename=None, + *args, **kwds): + """Create SlicesShellFrame instance.""" + frame.Frame.__init__(self, parent, id, title, pos, size, style,shellName='PySlices') + frame.ShellFrameMixin.__init__(self, config, dataDir) + + if size == wx.DefaultSize: + self.SetSize((750, 525)) + + intro = 'PySlices %s - The Flakiest Python Shell... Cut Up!' % VERSION + self.SetStatusText(intro.replace('\n', ', ')) + self.sliceshell = SlicesShell(parent=self, id=-1, introText=intro, + locals=locals, InterpClass=InterpClass, + startupScript=self.startupScript, + execStartupScript=self.execStartupScript, + showPySlicesTutorial=self.showPySlicesTutorial, + enableShellMode=self.enableShellMode, + hideFoldingMargin=self.hideFoldingMargin, + *args, **kwds) + self.buffer = self.sliceshell.buffer + + # Override the shell so that status messages go to the status bar. + self.sliceshell.setStatusText = self.SetStatusText + + self.sliceshell.SetFocus() + self.LoadSettings() + + self.currentDirectory = os.path.expanduser('~') + + if filename!=None: + self.bufferOpen(filename) + + self.Bind(wx.EVT_IDLE, self.OnIdle) + + + def OnClose(self, event): + """Event handler for closing.""" + self.bufferClose() + # This isn't working the way I want, but I'll leave it for now. + #if self.sliceshell.waiting: + # if event.CanVeto(): + # event.Veto(True) + #else: + # # TODO: Add check for saving + # self.SaveSettings() + # self.sliceshell.destroy() + # self.Destroy() + + def OnAbout(self, event): + """Display an About window.""" + title = 'About PySliceShell' + text = 'PySliceShell %s\n\n' % VERSION + \ + 'Yet another Python shell, only flakier.\n\n' + \ + 'Half-baked by Patrick K. O\'Brien,\n' + \ + 'the other half is still in the oven.\n\n' + \ + 'Shell Revision: %s\n' % self.shell.revision + \ + 'Interpreter Revision: %s\n\n' % self.shell.interp.revision + \ + 'Platform: %s\n' % sys.platform + \ + 'Python Version: %s\n' % sys.version.split()[0] + \ + 'wxPython Version: %s\n' % wx.VERSION_STRING + \ + ('\t(%s)\n' % ", ".join(wx.PlatformInfo[1:])) + dialog = wx.MessageDialog(self, text, title, + wx.OK | wx.ICON_INFORMATION) + dialog.ShowModal() + dialog.Destroy() + + + def OnHelp(self, event): + """Show a help dialog.""" + frame.ShellFrameMixin.OnHelp(self, event) + + + def LoadSettings(self): + if self.config is not None: + frame.ShellFrameMixin.LoadSettings(self) + frame.Frame.LoadSettings(self, self.config) + self.sliceshell.LoadSettings(self.config) + + def SaveSettings(self, force=False): + if self.config is not None: + frame.ShellFrameMixin.SaveSettings(self,force) + if self.autoSaveSettings or force: + frame.Frame.SaveSettings(self, self.config) + self.sliceshell.SaveSettings(self.config) + + def DoSaveSettings(self): + if self.config is not None: + self.SaveSettings(force=True) + self.config.Flush() + + def OnEnableShellMode(self,event): + """Change between Slices Mode and Shell Mode""" + frame.Frame.OnEnableShellMode(self,event) + self.sliceshell.ToggleShellMode(self.enableShellMode) + + def OnHideFoldingMargin(self,event): + """Change between Slices Mode and Shell Mode""" + frame.Frame.OnHideFoldingMargin(self,event) + self.sliceshell.ToggleFoldingMargin(self.hideFoldingMargin) + # Copied Straight from crustslices.py (update both with any changes...) + # Stolen Straight from editor.EditorFrame + # Modified a little... :) + # || + # \/ + def OnIdle(self, event): + """Event handler for idle time.""" + self._updateTitle() + event.Skip() + + def _updateTitle(self): + """Show current title information.""" + title = self.GetTitle() + if self.bufferHasChanged(): + if title.startswith('* '): + pass + else: + self.SetTitle('* ' + title) + else: + if title.startswith('* '): + self.SetTitle(title[2:]) + + def hasBuffer(self): + """Return True if there is a current buffer.""" + if self.buffer: + return True + else: + return False + + def bufferClose(self): + """Close buffer.""" + if self.buffer.hasChanged(): + cancel = self.bufferSuggestSave() + if cancel: + #event.Veto() + return cancel + self.SaveSettings() + self.sliceshell.destroy() + self.bufferDestroy() + self.Destroy() + + return False + + def bufferCreate(self, filename=None): + """Create new buffer.""" + self.bufferDestroy() + buffer = Buffer() + self.panel = panel = wx.Panel(parent=self, id=-1) + panel.Bind (wx.EVT_ERASE_BACKGROUND, lambda x: x) + editor = Editor(parent=panel) + panel.editor = editor + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(editor.window, 1, wx.EXPAND) + panel.SetSizer(sizer) + panel.SetAutoLayout(True) + sizer.Layout() + buffer.addEditor(editor) + buffer.open(filename) + self.setEditor(editor) + self.editor.setFocus() + self.SendSizeEvent() + + + def bufferDestroy(self): + """Destroy the current buffer.""" + if self.buffer: + self.editor = None + self.buffer = None + + + def bufferHasChanged(self): + """Return True if buffer has changed since last save.""" + if self.buffer: + return self.buffer.hasChanged() + else: + return False + + def bufferNew(self): + """Create new buffer.""" + cancel = self.bufferSuggestSave() + if cancel: + return cancel + #self.bufferCreate() + self.clear() + self.SetTitle( 'PySlices') + self.sliceshell.NeedsCheckForSave=False + self.sliceshell.SetSavePoint() + self.buffer.doc = document.Document() + self.buffer.name = 'This shell' + self.buffer.modulename = self.buffer.doc.filebase + cancel = False + return cancel + + def bufferOpen(self,file=None): + """Open file in buffer.""" + if self.bufferHasChanged(): + cancel = self.bufferSuggestSave() + if cancel: + return cancel + + if file==None: + file=wx.FileSelector('Open a PySlices File', + wildcard='*.pyslices', + default_path=self.currentDirectory) + if file!=None and file!=u'': + fid=open(file,'r') + self.sliceshell.LoadPySlicesFile(fid) + fid.close() + self.currentDirectory = os.path.split(file)[0] + self.SetTitle( os.path.split(file)[1] + ' - PySlices') + self.sliceshell.NeedsCheckForSave=False + self.sliceshell.SetSavePoint() + self.buffer.doc = document.Document(file) + self.buffer.name = self.buffer.doc.filename + self.buffer.modulename = self.buffer.doc.filebase + self.sliceshell.ScrollToLine(0) + return + +## def bufferPrint(self): +## """Print buffer.""" +## pass + +## def bufferRevert(self): +## """Revert buffer to version of file on disk.""" +## pass + + # was self.buffer.save(self): # """Save buffer.""" + def simpleSave(self,confirmed=False): + filepath = self.buffer.doc.filepath + self.buffer.confirmed = confirmed + if not filepath: + return # XXX Get filename + if not os.path.exists(filepath): + self.buffer.confirmed = True + if not self.buffer.confirmed: + self.buffer.confirmed = self.buffer.overwriteConfirm(filepath) + if self.buffer.confirmed: + try: + fid = open(filepath, 'wb') + self.sliceshell.SavePySlicesFile(fid) + finally: + if fid: + fid.close() + self.sliceshell.SetSavePoint() + self.SetTitle( os.path.split(filepath)[1] + ' - PySlices') + self.sliceshell.NeedsCheckForSave=False + + def bufferSave(self): + """Save buffer to its file.""" + if self.buffer.doc.filepath: + # self.buffer.save() + self.simpleSave(confirmed=True) + cancel = False + else: + cancel = self.bufferSaveAs() + return cancel + + def bufferSaveAs(self): + """Save buffer to a new filename.""" + if self.bufferHasChanged() and self.buffer.doc.filepath: + cancel = self.bufferSuggestSave() + if cancel: + return cancel + filedir = '' + if self.buffer and self.buffer.doc.filedir: + filedir = self.buffer.doc.filedir + result = editor.saveSingle(title='Save PySlices File',directory=filedir, + wildcard='PySlices Files (*.pyslices)|*.pyslices') + if result.path not in ['',None]: + if result.path[-9:]!=".pyslices": + result.path+=".pyslices" + + self.buffer.doc = document.Document(result.path) + self.buffer.name = self.buffer.doc.filename + self.buffer.modulename = self.buffer.doc.filebase + self.simpleSave(confirmed=True) # allow overwrite + cancel = False + else: + cancel = True + return cancel + + def bufferSaveACopy(self): + """Save buffer to a new filename.""" + filedir = '' + if self.buffer and self.buffer.doc.filedir: + filedir = self.buffer.doc.filedir + result = editor.saveSingle(title='Save a Copy of PySlices File',directory=filedir, + wildcard='PySlices Files (*.pyslices)|*.pyslices') + + if result.path not in ['',None]: + if result.path[-9:]!=".pyslices": + result.path+=".pyslices" + + # if not os.path.exists(result.path): + try: # Allow overwrite... + fid = open(result.path, 'wb') + self.sliceshell.SavePySlicesFile(fid) + finally: + if fid: + fid.close() + + cancel = False + else: + cancel = True + return cancel + + def bufferSuggestSave(self): + """Suggest saving changes. Return True if user selected Cancel.""" + result = editor.messageDialog(parent=None, + message='%s has changed.\n' + 'Would you like to save it first' + '?' % self.buffer.name, + title='Save current file?', + style=wx.YES_NO | wx.CANCEL | wx.NO_DEFAULT | + wx.CENTRE | wx.ICON_QUESTION ) + if result.positive: + cancel = self.bufferSave() + else: + cancel = result.text == 'Cancel' + return cancel + + def updateNamespace(self): + """Update the buffer namespace for autocompletion and calltips.""" + if self.buffer.updateNamespace(): + self.SetStatusText('Namespace updated') + else: + self.SetStatusText('Error executing, unable to update namespace') + + + +# TODO : Update the help text +HELP_TEXT = """\ +* Key bindings: +Home Go to the beginning of the line. +End Go to the end of the line. +Shift+Home Select to the beginning of the line. +Shift+End Select to the end of the line. +Ctrl-Home Jump to the beginning of the slice; + If already there, jump to beginning of previous slice +Ctrl-End Jump to the end of the slice; + If already there, jump to end of next slice +Ctrl-PageUp Jump to the beginning of the shell +Ctrl-PageDown Jump to the end of the shell +Ctrl+C Copy selected text, removing prompts. +Ctrl+Shift+C Copy selected text, retaining prompts. +Alt+C Copy to the clipboard, including prefixed prompts. +Ctrl+X Cut selected text. +Ctrl+V Paste from clipboard. +Ctrl+Shift+V Paste and run multiple commands from clipboard. +Ctrl+Up Arrow Retrieve Previous History item. +Alt+P Retrieve Previous History item. +Ctrl+Down Arrow Retrieve Next History item. +Alt+N Retrieve Next History item. +Shift+Up Arrow Insert Previous History item. +Shift+Down Arrow Insert Next History item. +F8 Command-completion of History item. + (Type a few characters of a previous command and press F8.) +Ctrl+] Increase font size. +Ctrl+[ Decrease font size. +Ctrl+= Default font size. + +Ctrl-Space Show Auto Completion. +Ctrl-Shift-Space Show Call Tip. +Ctrl-Shift-H Complete Text from History. + +Ctrl+F Search +Ctrl+G Search next +F12 on/off "free-edit" mode + For testing only -- This does not preserve markers! + +In "Slices Mode": +Return Insert new line +Enter (Numpad) Run command in slice +Ctrl+Return "" +Shift+Return "" + +In "Shell Mode": +Return or Enter Insert a new line +Ctrl+Return "" +Shift+Return "" +2 Returns in a row Run command in slice +""" + +class SlicesShellFacade: + """Simplified interface to all shell-related functionality. + + This is a semi-transparent facade, in that all attributes of other + are accessible, even though only some are visible to the user.""" + + name = 'SlicesShell Interface' + revision = __revision__ + + def __init__(self, other): + """Create a SlicesShellFacade instance.""" + d = self.__dict__ + d['other'] = other + d['helpText'] = HELP_TEXT + d['this'] = other.this + + def help(self): + """Display some useful information about how to use the slices shell.""" + self.write(self.helpText,type='Output') + + def __getattr__(self, name): + if hasattr(self.other, name): + return getattr(self.other, name) + else: + raise AttributeError, name + + def __setattr__(self, name, value): + if self.__dict__.has_key(name): + self.__dict__[name] = value + elif hasattr(self.other, name): + setattr(self.other, name, value) + else: + raise AttributeError, name + + def _getAttributeNames(self): + """Return list of magic attributes to extend introspection.""" + list = [ + 'about', + 'ask', + 'autoCallTip', + 'autoComplete', + 'autoCompleteAutoHide', + 'autoCompleteCaseInsensitive', + 'autoCompleteIncludeDouble', + 'autoCompleteIncludeMagic', + 'autoCompleteIncludeSingle', + 'callTipInsert', + 'clear', + 'pause', + 'prompt', + 'quit', + 'redirectStderr', + 'redirectStdin', + 'redirectStdout', + 'run', + 'runfile', + 'wrap', + 'zoom', + ] + list.sort() + return list + +DISPLAY_TEXT=""" +Author: %r +Py Version: %s +Py Slices Shell Revision: %s +Py Interpreter Revision: %s +Python Version: %s +wxPython Version: %s +wxPython PlatformInfo: %s +Platform: %s""" + +class SlicesShell(editwindow.EditWindow): + """Notebook Shell based on StyledTextCtrl.""" + + name = 'SlicesShell' + revision = __revision__ + + def __init__(self, parent, id=-1, pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.CLIP_CHILDREN, + introText='', locals=None, InterpClass=None, + startupScript=None, execStartupScript=True, + showPySlicesTutorial=True,enableShellMode=False, + hideFoldingMargin=False, *args, **kwds): + """Create Shell instance.""" + editwindow.EditWindow.__init__(self, parent, id, pos, size, style) + self.wrap() + if locals is None: + import __main__ + locals = __main__.__dict__ + + # Grab these so they can be restored by self.redirect* methods. + self.stdin = sys.stdin + self.stdout = sys.stdout + self.stderr = sys.stderr + + # Import a default interpreter class if one isn't provided. + if InterpClass == None: + from interpreter import Interpreter + else: + Interpreter = InterpClass + + # Create a replacement for stdin. + self.reader = PseudoFileIn(self.readline, self.readlines) + self.reader.input = '' + self.reader.isreading = False + + # Set up the interpreter. + self.interp = Interpreter(locals=locals, + rawin=self.raw_input, + stdin=self.reader, + stdout=PseudoFileOut(self.writeOut), + stderr=PseudoFileErr(self.writeErr), + *args, **kwds) + + # Set up the buffer. + self.buffer = Buffer() + self.id = self.GetId() + self.buffer.addEditor(self) + self.buffer.name='This shell' + self.NeedsCheckForSave=False + + # Find out for which keycodes the interpreter will autocomplete. + self.autoCompleteKeys = self.interp.getAutoCompleteKeys() + + # Keep track of the last non-continuation prompt positions. + # Removed all references to these... solved a lot of odd bugs... + # self.promptPosStart = 0 + # self.promptPosEnd = 0 + + # Keep track of multi-line commands. + self.more = False + + # Use Margins to track input / output / slice number + self.margins = True + + # For use with forced updates during long-running scripts + self.lastUpdate=None + + if self.margins: + # margin 1 is already defined for the line numbers + # may eventually change it back to 0 like it ought to be... + self.SetMarginType(2, stc.STC_MARGIN_SYMBOL) + self.SetMarginType(3, stc.STC_MARGIN_SYMBOL) + self.SetMarginType(4, stc.STC_MARGIN_SYMBOL) + self.SetMarginWidth(2, 22) + self.SetMarginWidth(3, 22) + self.SetMarginWidth(4, 12) + self.SetMarginSensitive(2,True) + self.SetMarginSensitive(3,True) + self.SetMarginSensitive(4,True) + self.SetProperty("fold", "1") + # tabs are bad, use spaces + self.SetProperty("tab.timmy.whinge.level", "4") + self.SetMargins(0,0) + + + self.SetMarginMask(2, GROUPING_MASK | 1<<GROUPING_SELECTING ) + # Display Markers -24... + self.SetMarginMask(3, IO_MASK | 1<<IO_SELECTING | 1<<READLINE_BG | 1<<INPUT_READLINE ) + self.SetMarginMask(4, stc.STC_MASK_FOLDERS) + # Set the mask for the line markers, too... + self.SetMarginMask(1, 0) + + if hideFoldingMargin: + self.SetMarginWidth(4, 0) + self.hideFoldingMargin=hideFoldingMargin + + sel_color="#E0E0E0" + grouping_color="black" + input_color="red" + output_color="blue" + + self.MarkerDefine(GROUPING_SELECTING, stc.STC_MARK_FULLRECT, + sel_color, sel_color) + self.MarkerDefine(IO_SELECTING, stc.STC_MARK_FULLRECT, + sel_color, sel_color) + + self.MarkerDefine(GROUPING_START, stc.STC_MARK_BOXMINUS, + "white", grouping_color) + self.MarkerDefine(GROUPING_START_FOLDED, stc.STC_MARK_BOXPLUS, + "white", grouping_color) + self.MarkerDefine(GROUPING_MIDDLE, stc.STC_MARK_VLINE, + "white", grouping_color) + self.MarkerDefine(GROUPING_END, stc.STC_MARK_LCORNER, + "white", grouping_color) + + self.MarkerDefine(READLINE_BG, stc.STC_MARK_FULLRECT, + wx.Colour(191,191,191), wx.Colour(191,191,191)) + self.MarkerDefine(INPUT_READLINE, stc.STC_MARK_CHARACTER+ord('<'), + input_color, wx.Colour(191,191,191)) + + if enableShellMode: + self.mode='ShellMode' + else: + self.mode='SlicesMode' + + self.execOnNextReturn=False + if self.mode=='SlicesMode': + self.MarkerDefine(INPUT_START, stc.STC_MARK_BOXMINUS, + "white", input_color) + self.MarkerDefine(INPUT_START_FOLDED, stc.STC_MARK_BOXPLUS, + "white", input_color) + self.MarkerDefine(INPUT_MIDDLE, stc.STC_MARK_VLINE, + "white", input_color) + self.MarkerDefine(INPUT_END, stc.STC_MARK_LCORNER, + "white", input_color) + elif self.mode=='ShellMode': + self.MarkerDefine(INPUT_START, stc.STC_MARK_ARROWS, + input_color, "white") + self.MarkerDefine(INPUT_START_FOLDED, stc.STC_MARK_BOXPLUS, + "white", input_color) + self.MarkerDefine(INPUT_MIDDLE, stc.STC_MARK_DOTDOTDOT, + input_color, "white") + self.MarkerDefine(INPUT_END, stc.STC_MARK_DOTDOTDOT, + input_color, "white") + + self.MarkerDefine(OUTPUT_START, stc.STC_MARK_BOXMINUS, + "white", output_color) + self.MarkerDefine(OUTPUT_START_FOLDED, stc.STC_MARK_BOXPLUS, + "white", output_color) + self.MarkerDefine(OUTPUT_MIDDLE, stc.STC_MARK_VLINE, + "white", output_color) + self.MarkerDefine(OUTPUT_END, stc.STC_MARK_LCORNER, + "white", output_color) + + self.MarkerDefine(OUTPUT_BG, stc.STC_MARK_BACKGROUND, + "white", wx.Colour(242,242,255)) + + # Markers for folding margin... + self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, + "white", "#808080") + self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, + "white", "#808080") + self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, + "white", "#808080") + self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, + "white", "#808080") + self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, + "white", "#808080") + self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, + "white", "#808080") + self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, + "white", "#808080") + + # Create the command history. Commands are added into the + # front of the list (ie. at index 0) as they are entered. + # self.historyIndex is the current position in the history; it + # gets incremented as you retrieve the previous command, + # decremented as you retrieve the next, and reset when you hit + # Enter. self.historyIndex == -1 means you're on the current + # command, not in the history. + self.history = [] + self.historyIndex = -1 + + #DNM -- disable these markers... + #seb add mode for "free edit" + self.noteMode = 0 + #self.MarkerDefine(0,stc.STC_MARK_ROUNDRECT) # marker for hidden + self.searchTxt = "" + + # Assign handlers for keyboard events. + self.Bind(wx.EVT_CHAR, self.OnChar) + self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) + + self.Bind(wx.stc.EVT_STC_MARGINCLICK, self.OnMarginClick) + # TODO : Add a general functions to handle mouse clicks in the + # TODO: STC window whose sole purpose is to make it so + # TODO: that margin selection becomes unselected... + + # Assign handler for the context menu + self.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu) + self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI) + + # Assign handlers for edit events + self.Bind(wx.EVT_MENU, lambda evt: self.Cut(), id=wx.ID_CUT) + self.Bind(wx.EVT_MENU, lambda evt: self.Copy(), id=wx.ID_COPY) + self.Bind(wx.EVT_MENU, lambda evt: self.CopyWithPrompts(), id=frame.ID_COPY_PLUS) + self.Bind(wx.EVT_MENU, lambda evt: self.Paste(), id=wx.ID_PASTE) + self.Bind(wx.EVT_MENU, lambda evt: self.PasteAndRun(), id=frame.ID_PASTE_PLUS) + self.Bind(wx.EVT_MENU, lambda evt: self.SelectAll(), id=wx.ID_SELECTALL) + self.Bind(wx.EVT_MENU, lambda evt: self.Clear(), id=wx.ID_CLEAR) + self.Bind(wx.EVT_MENU, lambda evt: self.Undo(), id=wx.ID_UNDO) + self.Bind(wx.EVT_MENU, lambda evt: self.Redo(), id=wx.ID_REDO) + + # Assign handler for idle time. + self.waiting = False + self.Bind(wx.EVT_IDLE, self.OnIdle) + + # Display the introductory banner information. + self.showIntro(introText) + + outStart,outEnd,inStart,inMiddle,inEnd = [[],[],[],[],[]] + + # Make "executed startup script move to the top..." + if showPySlicesTutorial: + self.write(tutorialText,'Output') + tutStart=5 + testStart=16 + outStart=[tutStart,testStart+3] + outEnd=[tutStart-1,testStart-1] + inStart=[testStart] + inMiddle=[testStart+1] + inEnd=[testStart+2] + + # Assign some pseudo keywords to the interpreter's namespace. + self.setBuiltinKeywords() + + # Add 'shell' to the interpreter's local namespace. + self.setLocalShell() + + # Do this last so the user has complete control over their + # environment. They can override anything they want. + if execStartupScript: + if startupScript is None: + startupScript = os.environ.get('PYTHONSTARTUP') + self.execStartupScript(startupScript) + else: + self.prompt() + + outStart+=[0] + outEnd+=[self.GetLineCount()-2] + inStart+=[self.GetLineCount()-1] + # Set all the line markers to the proper initial states... + for i in range(self.GetLineCount()): + self.clearGroupingMarkers(i) + self.clearIOMarkers(i) + if i in outStart: + self.MarkerAdd(i,GROUPING_START) + self.MarkerAdd(i,OUTPUT_START) + # Background color is confusing for tutorial... skip it! + #self.MarkerAdd(i,OUTPUT_BG) + elif i in outEnd: + self.MarkerAdd(i,GROUPING_END) + self.MarkerAdd(i,OUTPUT_END) + #self.MarkerAdd(i,OUTPUT_BG) + elif i in inStart: + self.MarkerAdd(i,GROUPING_START) + self.MarkerAdd(i,INPUT_START) + elif i in inMiddle: + self.MarkerAdd(i,GROUPING_MIDDLE) + self.MarkerAdd(i,INPUT_MIDDLE) + elif i in inEnd: + self.MarkerAdd(i,GROUPING_END) + self.MarkerAdd(i,INPUT_END) + else: + self.MarkerAdd(i,GROUPING_MIDDLE) + self.MarkerAdd(i,OUTPUT_MIDDLE) + #self.MarkerAdd(i,OUTPUT_BG) + + self.SliceSelection=False + self.runningSlice=None + + ## NOTE: See note at bottom of this file... + ## #seb: File drag and drop + ## self.SetDropTarget( FileDropTarget(self) ) + + #ADD UNDO + # Everywhere "ADD UNDO" appears, there is new code to handle markers + self.EmptyUndoBuffer() + + wx.CallAfter(self.ScrollToLine, 0) + + def ToggleShellMode(self,enableShellMode=None): + if enableShellMode==None: + if self.mode=='ShellMode': self.mode='SlicesMode' + elif self.mode=='SlicesMode': self.mode='ShellMode' + elif enableShellMode: + self.mode='ShellMode' + else: + self.mode='SlicesMode' + + input_color="red" + if self.mode=='SlicesMode': + self.MarkerDefine(INPUT_START, stc.STC_MARK_BOXMINUS, + "white", input_color) + self.MarkerDefine(INPUT_START_FOLDED, stc.STC_MARK_BOXPLUS, + "white", input_color) + self.MarkerDefine(INPUT_MIDDLE, stc.STC_MARK_VLINE, + "white", input_color) + self.MarkerDefine(INPUT_END, stc.STC_MARK_LCORNER, + "white", input_color) + elif self.mode=='ShellMode': + self.MarkerDefine(INPUT_START, stc.STC_MARK_ARROWS, + input_color, "white") + self.MarkerDefine(INPUT_START_FOLDED, stc.STC_MARK_BOXPLUS, + "white", input_color) + self.MarkerDefine(INPUT_MIDDLE, stc.STC_MARK_DOTDOTDOT, + input_color, "white") + self.MarkerDefine(INPUT_END, stc.STC_MARK_DOTDOTDOT, + input_color, "white") + + def ToggleFoldingMargin(self,hideFoldingMargin=None): + if hideFoldingMargin==None: + self.hideFoldingMargin = not self.hideFoldingMargin + else: + self.hideFoldingMargin = hideFoldingMargin + + if self.hideFoldingMargin: + self.SetMarginWidth(4, 0) + else: + self.SetMarginWidth(4, 12) + + def clearHistory(self): + self.history = [] + self.historyIndex = -1 + dispatcher.send(signal="SlicesShell.clearHistory") + + + def destroy(self): + del self.interp + + def setFocus(self): + """Set focus to the slices shell.""" + self.SetFocus() + + def OnIdle(self, event): + """Free the CPU to do other things.""" + if self.waiting: + time.sleep(0.05) + event.Skip() + + def showIntro(self, text=''): + """Display introductory text in the slices shell.""" + if text: + self.write(text,type='Output') + try: + if self.interp.introText: + if text and not text.endswith(os.linesep): + self.write(os.linesep,type='Output') + self.write(self.interp.introText,type='Output') + except AttributeError: + pass + + def setBuiltinKeywords(self): + """Create pseudo keywords as part of builtins. + + This sets "close", "exit" and "quit" to a helpful string. + """ + import __builtin__ + __builtin__.close = __builtin__.exit = __builtin__.quit = \ + 'Click on the close button to leave the application.' + __builtin__.cd = cd + __builtin__.ls = ls + __builtin__.pwd = pwd + __builtin__.sx = sx + + + def quit(self): + """Quit the application.""" + # XXX Good enough for now but later we want to send a close event. + # In the close event handler we can make sure they want to + # quit. Other applications, like PythonCard, may choose to + # hide rather than quit so we should just post the event and + # let the surrounding app decide what it wants to do. + self.write('Click on the close button to leave the application.', + type='Output') + + + def setLocalShell(self): + """Add 'slicesshell' to locals as reference to ShellFacade instance.""" + self.interp.locals['slicesshell'] = SlicesShellFacade(other=self) + + + def execStartupScript(self, startupScript): + """Execute the user's PYTHONSTARTUP script if they have one.""" + if startupScript and os.path.isfile(startupScript): + text = 'Startup script executed: ' + startupScript + self.push('print %r; execfile(%r)' % (text, startupScript)) + self.interp.startupScript = startupScript + else: + self.push('') + + + def about(self): + """Display information about Py.""" + text = DISPLAY_TEXT % \ + (__author__, VERSION, self.revision, self.interp.revision, + sys.version.split()[0], wx.VERSION_STRING, str(wx.PlatformInfo), + sys.platform) + self.write(text.strip(),type='Output') + + def BreakTextIntoCommands(self,text): + """Turn a text block into multiple multi-line commands.""" + + #text = text.lstrip() # This should not be done! + text = self.fixLineEndings(text) + text = self.lstripPrompt(text) + text = text.replace(os.linesep, '\n') + lines = text.split('\n') + + continuations = testForContinuations(text) + + if len(continuations)==2: # Error case... + return None,continuations[1] + elif len(continuations)==4: + stringContinuationList,indentationBlockList, \ + lineContinuationList,parentheticalContinuationList = continuations + + commands = [] + command = '' + for j,line in enumerate(lines): + lstrip = line.lstrip() + + # Get the first alnum word: + first_word=[] + for i in lstrip: + if i.isalnum(): + first_word.append(i) + else: + break + first_word = ''.join(first_word) + + # Continue the command if it is blank, has indentation, + # starts with else, elif,except, or finally + # or previous line had a line continuation \ + + if j==0: + stringCont = False + lineCont=False + else: + stringCont = stringContinuationList[j-1] + lineCont = lineContinuationList[j-1] + + if line.strip() == '' or lstrip != line or \ + first_word in ['else','elif','except','finally'] or \ + stringCont or lineCont: + # Multiline command. Add to the command. + command += '\n' + command += line + else: + # New command. + if command: + # Add the previous command to the list. + commands.append(command) + # Start a new command, which may be multiline. + command = line + + commands.append(command) + + return commands + + def MarkerSet(self,line,markerBitsSet): + """MarkerSet is the Set command for MarkerGet""" + markerBits=self.MarkerGet(line) + + numMarkers=14 + for i in range(numMarkers): + if (markerBitsSet & (1<<i)) and not (markerBits & (1<<i)): + self.MarkerAdd(line,i) + elif not (markerBitsSet & (1<<i)) and (markerBits & (1<<i)): + self.MarkerDelete(line,i) + def GetGroupingSlice(self,line_num=None): + """Get the start/stop lines for the slice based on any line in the slice""" + if line_num==None: + line_num=self.GetCurrentLine() + + num_lines=self.GetLineCount() + + for i in range(line_num,-1,-1): + if self.MarkerGet(i) & (1<<GROUPING_START | 1<<GROUPING_START_FOLDED): + break + start_line=i + + addition=0 + + for i in range(line_num,num_lines): + if self.MarkerGet(i) & 1<<GROUPING_END: + break + elif (i>line_num) and ( self.MarkerGet(i) + & (1<<GROUPING_START | 1<<GROUPING_START_FOLDED) ): + addition=-1 + break # the solo case... + stop_line=i+addition + + return start_line,stop_line + + def GetIOSlice(self,line_num=None): + """Get the start/stop lines for the slice based on any line in the slice""" + if line_num==None: + line_num=self.GetCurrentLine() + + num_lines=self.GetLineCount() + + for i in range(line_num,-1,-1): + if self.MarkerGet(i) & IO_ANY_START_MASK: + break + start_line=i + + addition=0 + + for i in range(line_num,num_lines): + if self.MarkerGet(i) & IO_END_MASK: + break + elif (i>line_num) and (self.MarkerGet(i) & IO_ANY_START_MASK): + addition=-1 + break # the solo case... + stop_line=i+addition + + return start_line,stop_line + + def FoldGroupingSlice(self,line_num=None): + if line_num==None: + line_num=self.GetCurrentLine() + + start,end=self.GetGroupingSlice(line_num) + self.HideLines(start+1,end) + marker=self.MarkerGet(start) + self.clearGroupingMarkers(start) + self.MarkerAdd(start,GROUPING_START_FOLDED) + self.clearIOMarkers(start) + if marker & ( 1<<INPUT_START | 1<<INPUT_START_FOLDED ): + self.MarkerAdd(start,INPUT_START_FOLDED) + elif marker & ( 1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED ): + self.MarkerAdd(start,OUTPUT_START_FOLDED) + self.MarkerAdd(start,OUTPUT_BG) + else: + pass #print 'Bad Markers!!!' + def FoldIOSlice(self,line_num=None): + if line_num==None: + line_num=self.GetCurrentLine() + + start,end=self.GetIOSlice(line_num) + self.HideLines(start+1,end) + marker=self.MarkerGet(start) + if (self.MarkerGet(start) & \ + (1<<GROUPING_START | 1<<GROUPING_START_FOLDED )) and \ + (self.MarkerGet(end) & 1<<GROUPING_END): + self.clearGroupingMarkers(start) + self.MarkerAdd(start,GROUPING_START_FOLDED) + self.clearIOMarkers(start) + if marker & ( 1<<INPUT_START | 1<<INPUT_START_FOLDED ): + self.MarkerAdd(start,INPUT_START_FOLDED) + elif marker & ( 1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED ): + self.MarkerAdd(start,OUTPUT_START_FOLDED) + self.MarkerAdd(start,OUTPUT_BG) + else: + pass #print 'Bad Markers!!!' + def UnFoldGroupingSlice(self,line_num=None): + if line_num==None: + line_num=self.GetCurrentLine() + + start,end=self.GetGroupingSlice(line_num) + self.ShowLines(start+1,end) + self.clearGroupingMarkers(start) + self.MarkerAdd(start,GROUPING_START) + for i in range(start,end): + marker=self.MarkerGet(i) + if marker & (1<<INPUT_START | 1<<INPUT_START_FOLDED): + self.clearIOMarkers(i) + self.MarkerAdd(i,INPUT_START) + elif marker & (1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED): + self.clearIOMarkers(i) + self.MarkerAdd(i,OUTPUT_START) + self.MarkerAdd(i,OUTPUT_BG) + + def UnFoldIOSlice(self,line_num=None): + if line_num==None: + line_num=self.GetCurrentLine() + + start,end=self.GetIOSlice(line_num) + self.ShowLines(start+1,end) + marker=self.MarkerGet(start) + if (self.MarkerGet(start) & \ + (1<<GROUPING_START | 1<<GROUPING_START_FOLDED )) and \ + (self.MarkerGet(end) & 1<<GROUPING_END): + self.clearGroupingMarkers(start) + self.MarkerAdd(start,GROUPING_START) + self.clearIOMarkers(start) + if marker & 1<<INPUT_START_FOLDED: + self.MarkerAdd(start,INPUT_START) + elif marker & 1<<OUTPUT_START_FOLDED: + self.MarkerAdd(start,OUTPUT_START) + self.MarkerAdd(start,OUTPUT_BG) + + def DeleteOutputSlicesAfter(self,line_num=None): + """Delete all outputs after an input""" + if line_num==None: + line_num=self.GetCurrentLine() + + num_lines=self.GetLineCount() + + if self.MarkerGet(line_num) & OUTPUT_MASK: + #print 'You can only run "DeleteOutputSlicesAfter" from an Input slice!' + return + + startIn,endIn=self.GetIOSlice(line_num) + startGrouping,endGrouping=self.GetGroupingSlice(line_num) + + if endIn<endGrouping: + self.SetSelection(self.PositionFromLine(endIn+1), + self.PositionFromLine(endGrouping+1)) + self.ReplaceSelection('',sliceDeletion=True) + + new_pos=self.GetLineEndPosition(line_num) + self.SetCurrentPos(new_pos) + self.SetSelection(new_pos,new_pos) + + def SplitSlice(self,line_num=None): + if line_num==None: + line_num=self.GetCurrentLine() + + start_num,end_num=self.GetIOSlice(line_num) + + if self.MarkerGet(line_num) & INPUT_MASK: + type='Input' + start=INPUT_START + end=INPUT_END + splitGrouping=True + elif self.MarkerGet(line_num) & OUTPUT_MASK: + type='Output' + start=OUTPUT_START + end=OUTPUT_END + splitGrouping=False + + if start_num==end_num: + return # Can't split one line! + elif start_num==line_num: + self.clearIOMarkers(line_num+1) + self.MarkerAdd(line_num+1,start) + if type=='Output': self.MarkerAdd(line_num+1,OUTPUT_BG) + if splitGrouping: + self.clearGroupingMarkers(line_num+1) + self.MarkerAdd(line_num+1,GROUPING_START) + else: + self.clearIOMarkers(line_num) + self.MarkerAdd(line_num,start) + if type=='Output': self.MarkerAdd(line_num,OUTPUT_BG) + if splitGrouping: + self.clearGroupingMarkers(line_num) + self.MarkerAdd(line_num,GROUPING_START) + if line_num-1>start_num: + self.clearIOMarkers(line_num-1) + self.MarkerAdd(line_num-1,end) + if type=='Output': self.MarkerAdd(line_num-1,OUTPUT_BG) + if splitGrouping: + self.clearGroupingMarkers(line_num-1) + self.MarkerAdd(line_num-1,GROUPING_END) + + def BackspaceWMarkers(self,force=False): + # Warning: This is not good at checking for bad markers! + c_before=self.GetCharAt(self.GetCurrentPos() - 1) + c_after=self.GetCharAt(self.GetCurrentPos()) + + if c_before==0: + # Disallow deleting the first line or it will destroy the markers... + return False + elif c_before in (ord('\n'),ord('\r')): + line_num=self.GetCurrentLine() + + marker=self.MarkerGet(line_num) + marker_before=self.MarkerGet(line_num-1) + marker_after=self.MarkerGet(line_num+1) + if marker_before & ( 1<<GROUPING_END ) : + return False # Disallow deleting lines between slices... + elif marker & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) : + return False # Disallow deleting lines between slices... + else: + if marker_before & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) : + self.clearGroupingMarkers(line_num) + elif marker & ( 1<<GROUPING_END ) : + self.clearGroupingMarkers(line_num-1) + + if (marker_before & 1<<INPUT_END) and force: + # Special case for use in processLine + self.clearIOMarkers(line_num) + elif marker_before & (1<<INPUT_END | 1<<OUTPUT_END): + return False # Disallow deleting lines between slices... + elif marker & ( 1<<INPUT_START | 1<<INPUT_START_FOLDED ) : + return False # Disallow deleting lines between slices... + else: + if marker_before & (1<<INPUT_START | 1<<INPUT_START_FOLDED | + 1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED): + self.clearIOMarkers(line_num) + elif marker & ( 1<<INPUT_END | 1<<OUTPUT_END ) : + self.clearIOMarkers(line_num-1) + + return True # If everything went well, return True and do the delete... + + def ForwardDeleteWMarkers(self): + c_before=self.GetCharAt(self.GetCurrentPos() - 1) + c_after=self.GetCharAt(self.GetCurrentPos()) + if c_after==0: + # Disallow deleting the first line or it will destroy the markers... + return False + elif c_after in (ord('\n'),ord('\r')): + line_num=self.GetCurrentLine() + + marker=self.MarkerGet(line_num) + marker_before=self.MarkerGet(line_num-1) + marker_after=self.MarkerGet(line_num+1) + if marker & ( 1<<GROUPING_END ) : + return False # Disallow deleting lines between slices... + elif marker_after & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) : + return False # Disallow deleting lines between slices... + else: + if marker & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) : + self.clearGroupingMarkers(line_num+1) + elif marker_after & ( 1<<GROUPING_END ) : + self.clearGroupingMarkers(line_num) + + if marker & ( 1<<INPUT_END | 1<<OUTPUT_END ) : + return False # Disallow deleting lines between slices... + elif marker_after & ( 1<<INPUT_START | 1<<INPUT_START_FOLDED ) : + return False # Disallow deleting lines between slices... + else: + if marker & (1<<INPUT_START | 1<<INPUT_START_FOLDED | + 1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED) : + self.clearIOMarkers(line_num+1) + elif marker_after & ( 1<<INPUT_END | 1<<OUTPUT_END ) : + self.clearIOMarkers(line_num) + + return True + + def GetIOSelection(self): + started=False + start=0 + end=self.GetLineCount()-1 + type=None + for i in range(self.GetLineCount()): + if self.MarkerGet(i) & 1<<IO_SELECTING: + if started==False: + start=i + if self.MarkerGet(i) & INPUT_MASK: + type='input' + elif self.MarkerGet(i) & OUTPUT_MASK: + type='output' + else: + if self.MarkerGet(i) & INPUT_MASK: + if type=='output': + end=i-1 + break + elif self.MarkerGet(i) & OUTPUT_MASK: + if type=='input': + end=i-1 + break + started=True + elif started==True: + end=i-1 + break + + if started==False: + #print 'No Selection!!' + self.SliceSelection=False + + return start,end + + def MergeAdjacentSlices(self): + """This function merges all adjacent selected slices.\n""" + \ + """Right now, only IO Merging is allowed.""" + started=False + start=0 + end=self.GetLineCount()-1 + type=None + for i in range(self.GetLineCount()): + if self.MarkerGet(i) & 1<<IO_SELECTING: + if started==False: + start=i + if self.MarkerGet(i) & INPUT_MASK: + type='input' + elif self.MarkerGet(i) & OUTPUT_MASK: + type='output' + else: + if self.MarkerGet(i) & INPUT_MASK: + if type=='output': + end=i-1 + break + else: + self.clearIOMarkers(i) + self.clearGroupingMarkers(i) + self.MarkerAdd(i,INPUT_MIDDLE) + self.MarkerAdd(i,GROUPING_MIDDLE) + elif self.MarkerGet(i) & OUTPUT_MASK: + if type=='input': + end=i-1 + break + else: + self.clearIOMarkers(i) + self.clearGroupingMarkers(i) + self.MarkerAdd(i,OUTPUT_MIDDLE) + self.MarkerAdd(i,OUTPUT_BG) + self.MarkerAdd(i,GROUPING_MIDDLE) + started=True + elif started==True: + end=i-1 + break + + if started and end!=start: + self.clearIOMarkers(end) + self.clearGroupingMarkers(end) + if type=='input': + self.MarkerAdd(end,INPUT_END) + if end+1<self.GetLineCount(): + if self.MarkerGet(end+1) & OUTPUT_MASK: + self.MarkerAdd(end,GROUPING_MIDDLE) + else: + self.MarkerAdd(end,GROUPING_END) + else: + self.MarkerAdd(end,GROUPING_END) + else: + if self.MarkerGet(start) & 1<<GROUPING_END: + self.clearGroupingMarkers(start) + self.MarkerAdd(start,GROUPING_MIDDLE) + self.MarkerAdd(end,OUTPUT_END) + self.MarkerAdd(end,OUTPUT_BG) + self.MarkerAdd(end,GROUPING_END) + + + def SliceSelectionDelete(self): + """Deletion of any selected and possibly discontinuous slices.""" + if not self.SliceSelection: + return + + # collect the line numbers to be deleted... + selectedSlices=[] + start,end=None,None + for i in range(self.GetLineCount()): + if self.MarkerGet(i) & (1<<GROUPING_SELECTING | 1<<IO_SELECTING): + if start==None: + start=i + end=i + elif start!=None: + selectedSlices.append([start,end]) + start,end=None,None + if start!=None: + selectedSlices.append([start,end]) + + # Unselect everything + self.MarginUnselectAll() + self.SliceSelection=False + + # Going in reverse, delete the selections, fixing the markers as we go... + for i in range(len(selectedSlices)-1,-1,-1): + self.SetSelection(self.PositionFromLine(selectedSlices[i][0]), + self.GetLineEndPosition(selectedSlices[i][1])+1) + + markerNext = self.MarkerGet(selectedSlices[i][1]+1) + + self.ReplaceSelection('',sliceDeletion=True) + + cur_line=self.GetCurrentLine() + + # If we've made a mess of the grouping markers, clean it up... + if ((self.MarkerGet(cur_line-1) & 1<<GROUPING_END) and + (self.MarkerGet(cur_line) & ( 1<<GROUPING_MIDDLE | 1<<GROUPING_END ) )): + self.clearGroupingMarkers(cur_line) + self.MarkerAdd(cur_line,GROUPING_START) + elif (( self.MarkerGet(cur_line-1) & 1<<GROUPING_MIDDLE ) and + ( self.MarkerGet(cur_line) & + ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) )): + self.clearGroupingMarkers(cur_line-1) + self.MarkerAdd(cur_line-1,GROUPING_END) + + if markerNext & 1<<OUTPUT_START: + self.clearIOMarkers(cur_line) + self.MarkerAdd(cur_line,OUTPUT_START) + self.MarkerAdd(cur_line,OUTPUT_BG) + elif markerNext & 1<<OUTPUT_START_FOLDED: + self.clearIOMarkers(cur_line) + self.MarkerAdd(cur_line,OUTPUT_START_FOLDED) + self.MarkerAdd(cur_line,OUTPUT_BG) + + return + + def OnChar(self, event): + """Keypress event handler. + + Only receives an event if OnKeyDown calls event.Skip() for the + corresponding event.""" + + if self.noteMode: + event.Skip() + return + + # Prevent modification of output slices + if not self.CanEdit(): + return + key = event.GetKeyCode() + currpos = self.GetCurrentPos() + stoppos = self.PositionFromLine(self.GetCurrentLine()) + + # Return (Enter) needs to be ignored in this handler. + if key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]: + pass + elif key in self.autoCompleteKeys: + # Usually the dot (period) key activates auto completion. + # Get the command between the prompt and the cursor. Add + # the autocomplete character to the end of the command. + if self.AutoCompActive(): + self.AutoCompCancel() + command = self.GetTextRange(stoppos, currpos) + chr(key) + + # write with undo wrapper... + cpos=self.GetCurrentPos() + s=chr(key) + #ADD UNDO + self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s), + forceNewAction=False) + self.write(s,type='Input') + self.UpdateUndoHistoryAfter() + + if self.autoComplete: + self.autoCompleteShow(command) + elif key == ord('('): + # The left paren activates a call tip and cancels an + # active auto completion. + if self.AutoCompActive(): + self.AutoCompCancel() + # Get the command between the prompt and the cursor. Add + # the '(' to the end of the command. + self.ReplaceSelection('') + command = self.GetTextRange(stoppos, currpos) + '(' + + # write with undo wrapper... + cpos=self.GetCurrentPos() + s='(' + self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s), + forceNewAction=True) + self.undoHistory[self.undoIndex]['allowsAppend']=True + self.write(s,type='Input') + self.UpdateUndoHistoryAfter() + + self.autoCallTipShow(command, + self.GetCurrentPos() == self.GetTextLength()) + else: + # Allow the normal event handling to take place. + # Use undo wrapper + cpos=self.GetCurrentPos() + try: + s=chr(key) + self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s)) + event.Skip() + self.UpdateUndoHistoryAfter() + except: + event.Skip() + + def AutoCompActiveCallback(self): + numChars=self.GetTextLength()-self.TotalLengthForAutoCompActiveCallback + if numChars==0: + self.undoIndex-=1 + del(self.undoHistory[-1]) + else: + uH=self.undoHistory + uI=self.undoIndex + cpos=uH[uI]['posStart'] + s=''.join([chr(self.GetCharAt(cpos+i)) for i in range(numChars)]) + s.replace(os.linesep,'\n') + self.undoHistory[self.undoIndex]['charList'] = s + self.undoHistory[self.undoIndex]['posEnd'] = cpos + numChars + self.undoHistory[self.undoIndex]['numLines'] = len(s.split('\n')) + self.UpdateUndoHistoryAfter() + + def OnKeyDown(self, event): + """Key down event handler.""" + + key = event.GetKeyCode() + # If the auto-complete window is up let it do its thing. + if self.AutoCompActive(): + if key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]: + cpos=self.GetCurrentPos() + self.UpdateUndoHistoryBefore('insert','dummy',cpos,cpos+5, + forceNewAction=True) + self.undoHistory[self.undoIndex]['allowsAppend'] = True + self.TotalLengthForAutoCompActiveCallback=self.GetTextLength() + event.Skip() + wx.CallAfter(self.AutoCompActiveCallback) + if key in [wx.WXK_DELETE,wx.WXK_BACK]: + self.AutoCompCancel() + else: + event.Skip() + return + + #DNM + # Prevent modification of output slices + controlDown = event.ControlDown() + altDown = event.AltDown() + shiftDown = event.ShiftDown() + currpos = self.GetCurrentPos() + endpos = self.GetTextLength() + selecting = self.GetSelectionStart() != self.GetSelectionEnd() + + if key == wx.WXK_F12: #seb + if self.noteMode: + # self.promptPosStart not used anyway - or ? +## # We don't need to do this any more! +## self.promptPosEnd = self.PositionFromLine(self.GetLineCount()-1 ) + +## len(str(sys.ps1)) +## self.GotoLine(self.GetLineCount()) +## self.GotoPos(self.promptPosEnd) +## self.prompt() #make sure we have a prompt + self.SetCaretForeground("black") + self.SetCaretWidth(1) #default + self.SetCaretPeriod(500) #default + else: + self.SetCaretForeground("red") + self.SetCaretWidth(4) + self.SetCaretPeriod(0) #steady + + self.noteMode = not self.noteMode + return + if self.noteMode: + event.Skip() + return + + doLineBreak=False + doSubmitCommand=False + doPass=False + # Return is used to insert a line break. + # In Shell Mode, hit Return or Enter twice to submit a command + if ((not controlDown and not shiftDown and not altDown) and + key in [wx.WXK_RETURN,]): + if self.mode=='SlicesMode': + doLineBreak=True + elif self.mode=='ShellMode': + startLine,endLine = self.GetIOSlice() + startpos = self.PositionFromLine(startLine) + endpos = self.GetLineEndPosition(endLine) + command = self.GetTextRange(startpos, endpos) + strCont,indentBlock,lineCont,parenCont = testForContinuations(command,ignoreErrors=True) + + lastLine = command.split('\n')[-1] + if lastLine.lstrip()=='': # all whitespace... + stillIndented=False + elif lastLine[0]==' ': + stillIndented=True + else: + stillIndented=False + + if strCont[-1] or indentBlock[-1] or lineCont[-1] or \ + parenCont[-1]: + doLineBreak=True + elif stillIndented: + new_pos=self.GetLineEndPosition(endLine) + self.SetCurrentPos(new_pos) + self.SetSelection(new_pos,new_pos) + doLineBreak=True + elif self.GetCurrentLine()!=endLine: + new_pos=self.GetLineEndPosition(endLine) + self.SetCurrentPos(new_pos) + self.SetSelection(new_pos,new_pos) + doPass = True + else: + doSubmitCommand=True + # Enter (Shift/Ctrl + Enter/Return) submits a command to the interpreter. + # In Shell Mode, hit Return or Enter twice to submit a command + elif ( key in [wx.WXK_NUMPAD_ENTER,] or + ( (shiftDown or controlDown) and key in [wx.WXK_RETURN, + wx.WXK_NUMPAD_ENTER] ) ): + if self.mode=='SlicesMode': + doSubmitCommand=True + elif self.mode=='ShellMode': + doLineBreak=True + + #Only relevant in ShellMode... + + if doPass: + pass + elif doLineBreak or doSubmitCommand: + if self.CallTipActive(): + self.CallTipCancel() + elif self.SliceSelection: + for i in range(self.GetLineCount()): + if self.MarkerGet(i) & 1<<GROUPING_SELECTING: + self.DoMarginClick(i, 2, shiftDown, controlDown) + break + elif self.MarkerGet(i) & 1<<IO_SELECTING: + self.DoMarginClick(i, 3, shiftDown, controlDown) + break + elif doLineBreak: + self.insertLineBreak() + #Only relevant in ShellMode... + elif doSubmitCommand: + self.DeleteOutputSlicesAfter() + self.processLine() + + # Let Ctrl-Alt-* get handled normally. + elif controlDown and altDown: + event.Skip() + + # Clear the current, unexecuted command. + elif key == wx.WXK_ESCAPE: + if self.CallTipActive(): + event.Skip() + # Clear the current command + elif key == wx.WXK_BACK and controlDown and shiftDown: + self.clearCommand() + + # Increase font size. + elif controlDown and key in (ord(']'), wx.WXK_NUMPAD_ADD): + dispatcher.send(signal='FontIncrease') + + # Decrease font size. + elif controlDown and key in (ord('['), wx.WXK_NUMPAD_SUBTRACT): + dispatcher.send(signal='FontDecrease') + + # Default font size. + elif controlDown and key in (ord('='), wx.WXK_NUMPAD_DIVIDE): + dispatcher.send(signal='FontDefault') + + # Cut to the clipboard. + elif (controlDown and key in (ord('X'), ord('x'))) \ + or (shiftDown and key == wx.WXK_DELETE): + self.Cut() + + # Copy to the clipboard. + elif controlDown and not shiftDown \ + and key in (ord('C'), ord('c'), wx.WXK_INSERT): + self.Copy() + + # Copy to the clipboard, including prompts. + elif controlDown and shiftDown \ + and key in (ord('C'), ord('c'), wx.WXK_INSERT): + self.CopyWithPrompts() + + # Copy to the clipboard, including prefixed prompts. + elif altDown and not controlDown \ + and key in (ord('C'), ord('c'), wx.WXK_INSERT): + self.CopyWithPromptsPrefixed() + + # Home needs to be aware of the prompt. + elif controlDown and key == wx.WXK_HOME: + # Go to the beginning of the IO Slice + curLine = self.GetCurrentLine() + IOstart = self.GetIOSlice(curLine)[0] + home = self.PositionFromLine(IOstart) + if currpos == home and \ + IOstart > 0: + home = self.PositionFromLine(self.GetIOSlice(curLine-1)[0]) + self.SetCurrentPos(home) + if not selecting and not shiftDown: + self.SetAnchor(home) + self.EnsureCaretVisible() + + elif controlDown and key == wx.WXK_END: + curLine = self.GetCurrentLine() + IOend = self.GetIOSlice(curLine)[1] + end = self.GetLineEndPosition(IOend) + if currpos == end and \ + IOend < self.GetLineCount()-1: + end = self.GetLineEndPosition(self.GetIOSlice(curLine+1)[1]) + self.SetCurrentPos(end) + if not selecting and not shiftDown: + self.SetAnchor(end) + self.EnsureCaretVisible() + + elif controlDown and key == wx.WXK_PAGEUP: + pos=0 + if currpos > pos: + self.SetCurrentPos(pos) + if not selecting and not shiftDown: + self.SetAnchor(pos) + self.EnsureCaretVisible() + + elif controlDown and key == wx.WXK_PAGEDOWN: + pos = self.GetLineEndPosition(self.GetLineCount()-1) + if currpos < pos: + self.SetCurrentPos(pos) + if not selecting and not shiftDown: + self.SetAnchor(pos) + self.EnsureCaretVisible() + + elif selecting and key not in NAVKEYS and not self.CanEdit(): + pass + + # Paste from the clipboard. + elif (controlDown and not shiftDown and key in (ord('V'), ord('v'))) \ + or (shiftDown and not controlDown and key == wx.WXK_INSERT): + self.Paste() + + # Paste from the clipboard, run commands. + elif controlDown and shiftDown and \ + key in (ord('V'), ord('v')) and self.CanEdit(): + self.PasteAndRun() + + # Replace with the previous command from the history buffer. + elif (controlDown and not shiftDown and key == wx.WXK_UP) \ + or (altDown and key in (ord('P'), ord('p'))) and self.CanEdit(): + self.OnHistoryReplace(step=+1) + + # Replace with the next command from the history buffer. + elif (controlDown and not shiftDown and key == wx.WXK_DOWN) \ + or (altDown and key in (ord('N'), ord('n'))) and self.CanEdit(): + self.OnHistoryReplace(step=-1) + + # Insert the previous command from the history buffer. + elif (controlDown and shiftDown and key == wx.WXK_UP) and \ + self.CanEdit(): + self.OnHistoryInsert(step=+1) + + # Insert the next command from the history buffer. + elif (controlDown and shiftDown and key == wx.WXK_DOWN) and \ + self.CanEdit(): + self.OnHistoryInsert(step=-1) + + # Ctrl-Space shows Auto Completion + # Ctrl-Shift-Space shows CallTips + elif controlDown and key == wx.WXK_SPACE: + self.OnCallTipAutoCompleteManually(shiftDown) + + # Ctrl+Shift+H is used to complete Text (from already typed words) + elif controlDown and shiftDown and key in [ord('H')]: + self.OnShowCompHistory() + + # Search up the history for the text in front of the cursor. + elif key == wx.WXK_F8: + self.OnHistorySearch() + + # Don't backspace over the latest non-continuation prompt. + elif key == wx.WXK_BACK: + if self.SliceSelection: + self.SliceSelectionDelete() + wx.CallAfter(self.RestoreFirstMarker) + elif selecting and self.CanEdit(): + self.ReplaceSelection('') + #event.Skip() + elif self.CanEdit(): + doDelete=True + cur_line=self.GetCurrentLine() + if not cur_line==0 and \ + self.GetCurrentPos()==self.PositionFromLine(cur_line): + if self.MarkerGet(cur_line-1) & OUTPUT_MASK: + doDelete=False + + if doDelete: + cpos=self.GetCurrentPos() + s=chr(self.GetCharAt(cpos-1)) + self.UpdateUndoHistoryBefore('delete',s,cpos-1,cpos) + if self.BackspaceWMarkers(): + event.Skip() + + wx.CallAfter(self.RestoreFirstMarker) + + elif key == wx.WXK_DELETE: + if self.SliceSelection: + self.SliceSelectionDelete() + wx.CallAfter(self.RestoreFirstMarker) + elif selecting and self.CanEdit(): + self.ReplaceSelection('') + #event.Skip() + elif self.CanEdit(): + doDelete=True + cur_line=self.GetCurrentLine() + if not cur_line==self.GetLineCount()-1 and \ + self.GetCurrentPos()==self.GetLineEndPosition(cur_line): + if self.MarkerGet(cur_line+1) & OUTPUT_MASK: + doDelete=False + + if doDelete: + cpos=self.GetCurrentPos() + s=chr(self.GetCharAt(cpos)) + self.UpdateUndoHistoryBefore('delete',s,cpos,cpos+1) + if self.ForwardDeleteWMarkers(): + event.Skip() + + wx.CallAfter(self.RestoreFirstMarker) + + # Only allow these keys after the latest prompt. + elif key == wx.WXK_TAB and self.CanEdit(): + # use the same mechanism as with autocmplete... + cpos=self.GetCurrentPos() + self.UpdateUndoHistoryBefore('insert','dummy',cpos,cpos+5, + forceNewAction=True) + self.undoHistory[self.undoIndex]['allowsAppend'] = True + self.TotalLengthForAutoCompActiveCallback=self.GetTextLength() + event.Skip() + wx.CallAfter(self.AutoCompActiveCallback) + + # Don't toggle between insert mode and overwrite mode. + elif key == wx.WXK_INSERT: + pass + + # Don't allow line deletion. + #elif controlDown and key in (ord('L'), ord('l')): + # TODO : Allow line deletion eventually ?? + #event.Skip() + # pass + + # Don't allow line transposition. + # Toggle Shell Mode / Slices Mode + elif controlDown and key in (ord('T'), ord('t')): + self.ToggleShellMode() + + #Open and Save now work when using CrustSlicesFrames + elif controlDown and key in (ord('L'), ord('l')): + #print 'Load it' + file=wx.FileSelector("Load File As New Slice") + if file!=u'': + fid=open(file,'r') + self.LoadPyFileAsSlice(fid) + fid.close() + + elif controlDown and key in (ord('D'), ord('d')): + #Disallow line duplication in favor of divide slices + if self.MarkerGet(self.GetCurrentLine()) & INPUT_MASK: + #ADD UNDO + cpos=self.GetCurrentPos() + start,end = map(self.PositionFromLine, + self.GetGroupingSlice(self.LineFromPosition(cpos))) + self.UpdateUndoHistoryBefore('marker','',start,end, + forceNewAction=True) + self.SplitSlice() + # Turn off selecting + self.SetSelection(cpos,cpos) + self.ReplaceSelection('') + self.UpdateUndoHistoryAfter() + + elif controlDown and key in (ord('M'), ord('m')): + #ADD UNDO + if self.SliceSelection: + cpos=self.GetCurrentPos() + ioSel=self.GetIOSelection() + if self.SliceSelection: + start,end = map(self.PositionFromLine,ioSel) + self.UpdateUndoHistoryBefore('marker','',start,end, + forceNewAction=True) + self.MergeAdjacentSlices() + # Turn off selecting + self.SetSelection(cpos,cpos) + self.ReplaceSelection('') + self.UpdateUndoHistoryAfter() + + + # Change arrow keys to allow margin behaviors... + elif self.SliceSelection and \ + key in [wx.WXK_UP,wx.WXK_DOWN,wx.WXK_RIGHT,wx.WXK_LEFT]: + # TODO : This is useful, but not optimal! + if key==wx.WXK_UP: + for i in range(self.GetLineCount()): + if self.MarkerGet(i) & 1<<GROUPING_SELECTING: + if i>0: #Grouping + self.DoMarginClick(i-1, 2, shiftDown, controlDown) + break + elif self.MarkerGet(i) & 1<<IO_SELECTING: + if i>0: #IO + self.DoMarginClick(i-1, 3, shiftDown, controlDown) + break + elif key==wx.WXK_DOWN: + for i in range(self.GetLineCount()-1,-1,-1): + if self.MarkerGet(i) & 1<<GROUPING_SELECTING: + if i<self.GetLineCount()-1: #Grouping + self.DoMarginClick(i+1, 2, shiftDown, controlDown) + break + elif self.MarkerGet(i) & 1<<IO_SELECTING: + if i<self.GetLineCount()-1: #IO + self.DoMarginClick(i+1, 3, shiftDown, controlDown) + break + elif key==wx.WXK_RIGHT: + for i in range(self.GetLineCount()): + if self.MarkerGet(i) & 1<<GROUPING_SELECTING: + self.DoMarginClick(i, 3, shiftDown, controlDown) + break + elif self.MarkerGet(i) & 1<<IO_SELECTING: + self.MarginUnselectAll() + # Go to the beginning of the IO Slice + self.SetCurrentPos(self.PositionFromLine(i)) + if not selecting and not shiftDown: + self.SetAnchor(self.PositionFromLine(i)) + self.EnsureCaretVisible() + break + elif key==wx.WXK_LEFT: + for i in range(self.GetLineCount()): + if self.MarkerGet(i) & 1<<GROUPING_SELECTING: + break + elif self.MarkerGet(i) & 1<<IO_SELECTING: + self.DoMarginClick(i, 2, shiftDown, controlDown) + break + # Basic navigation keys should work anywhere. + elif key in NAVKEYS: + event.Skip() + # Protect the readonly portion of the slices shell. + elif not self.CanEdit(): + pass + else: + # Check to see if we're selecting + if self.GetSelectionEnd()>self.GetSelectionStart(): + # Check to see if a normal input took place + if not controlDown and not altDown and key<256: + self.ReplaceSelection('') # This seems to work... + event.Skip() + + if self.SliceSelection: + if key not in [wx.WXK_UP,wx.WXK_DOWN,wx.WXK_RIGHT,wx.WXK_LEFT, + wx.WXK_ALT,wx.WXK_COMMAND,wx.WXK_CONTROL,wx.WXK_SHIFT]: + self.MarginUnselectAll() + + + def MarginSelectAll(self): + num_lines=self.GetLineCount() + for i in range(num_lines): + self.MarkerAdd(i,GROUPING_SELECTING) + self.MarkerDelete(i,IO_SELECTING) + + def MarginUnselectAll(self): + num_lines=self.GetLineCount() + for i in range(num_lines): + self.MarkerDelete(i,IO_SELECTING) + self.MarkerDelete(i,GROUPING_SELECTING) + self.SliceSelection=False + + def DoMarginClick(self, lineClicked, margin, shiftDown, controlDown): + num_lines=self.GetLineCount() + + if margin==1: + pass # these events are not sent right now... + if margin==2: + self.SliceSelection=True + start,end=self.GetGroupingSlice(lineClicked) + startPos=self.PositionFromLine(start) + self.SetCurrentPos(startPos) + self.SetSelection(startPos,startPos) + start_marker=self.MarkerGet(start) + if self.MarkerGet(lineClicked) & 1<<GROUPING_SELECTING: + toggle=self.MarkerDelete + if not shiftDown: + if start_marker & 1<<GROUPING_START: + self.FoldGroupingSlice(lineClicked) + elif start_marker & 1<<GROUPING_START_FOLDED: + self.UnFoldGroupingSlice(lineClicked) + else: + toggle=self.MarkerAdd + + if not shiftDown: + self.MarginUnselectAll() + + for i in range(start,end+1): + toggle(i,GROUPING_SELECTING) + elif margin==3: + self.SliceSelection=True + start,end=self.GetIOSlice(lineClicked) + startPos=self.PositionFromLine(start) + self.SetCurrentPos(startPos) + self.SetSelection(startPos,startPos) + start_marker=self.MarkerGet(start) + if self.MarkerGet(lineClicked) & 1<<IO_SELECTING: + toggle=self.MarkerDelete + if not shiftDown: + if start_marker & IO_START_MASK: + self.FoldIOSlice(lineClicked) + elif start_marker & IO_START_FOLDED_MASK: + self.UnFoldIOSlice(lineClicked) + else: + toggle=self.MarkerAdd + + if not shiftDown: + self.MarginUnselectAll() + + for i in range(start,end+1): + toggle(i,IO_SELECTING) + + #print start,end + + elif margin==4: + # TODO : Folding ?? + if 1:#self.MarkerGet(lineClicked) & ( 1<<7 | 1<<8 ): + if shiftDown: + self.SetFoldExpanded(lineClicked, True) + self.Expand(lineClicked, True, True, 1) + elif controlDown: + if self.GetFoldExpanded(lineClicked): + self.SetFoldExpanded(lineClicked, False) + self.Expand(lineClicked, False, True, 0) + else: + self.SetFoldExpanded(lineClicked, True) + self.Expand(lineClicked, True, True, 100) + else: + self.ToggleFold(lineClicked) + else: + self.MarginUnselectAll() + if margin in [2,3]: + if toggle==self.MarkerDelete and not shiftDown: + self.SliceSelection=False + else: + self.SliceSelection=True + + def OnMarginClick(self, evt): + + # fold and unfold as neededNAVKEYS + lineClicked = self.LineFromPosition(evt.GetPosition()) + self.DoMarginClick(lineClicked,evt.GetMargin(),evt.GetShift(),evt.GetControl()) + evt.Skip() + + def OnShowCompHistory(self): + """Show possible autocompletion Words from already typed words.""" + + #copy from history + his = self.history[:] + + #put together in one string + joined = " ".join (his) + import re + + #sort out only "good" words + newlist = re.split("[ \.\[\]=}(\)\,0-9\"]", joined) + + #length > 1 (mix out "trash") + thlist = [] + for i in newlist: + if len (i) > 1: + thlist.append (i) + + #unique (no duplicate words + #oneliner from german python forum => unique list + unlist = [thlist[i] for i in xrange(len(thlist)) if thlist[i] not in thlist[:i]] + + #sort lowercase + unlist.sort(lambda a, b: cmp(a.lower(), b.lower())) + + #this is more convenient, isn't it? + self.AutoCompSetIgnoreCase(True) + + #join again together in a string + stringlist = " ".join(unlist) + + #pos von 0 noch ausrechnen + + #how big is the offset? + cpos = self.GetCurrentPos() - 1 + while chr (self.GetCharAt (cpos)).isalnum(): + cpos -= 1 + + #the most important part + self.AutoCompShow(self.GetCurrentPos() - cpos -1, stringlist) + + def ReplaceSelection(self,text,sliceDeletion=False,*args,**kwds): + startIO,endIO=self.GetIOSlice() + startGrouping,endGrouping=self.GetGroupingSlice() + startSel = self.LineFromPosition(self.GetSelectionStart()) + endSel = self.LineFromPosition(self.GetSelectionEnd()) + + #ADD UNDO + cpos=self.GetSelectionStart() + s=self.GetSelectedText() + if s!='': + self.UpdateUndoHistoryBefore('delete',s,cpos,cpos+len(s), + forceNewAction=True) + editwindow.EditWindow.ReplaceSelection(self,'',*args,**kwds) + if s!='' and not sliceDeletion: + self.UpdateUndoHistoryAfter() + + if endSel-startSel>0 and not sliceDeletion: + if endSel==endIO and startIO!=self.GetCurrentLine(): + self.clearIOMarkers() + self.MarkerAdd(self.GetCurrentLine(),INPUT_END) + + if endSel==endGrouping and startGrouping!=self.GetCurrentLine(): + self.clearGroupingMarkers() + self.MarkerAdd(self.GetCurrentLine(),GROUPING_END) + + cpos=self.GetSelectionStart() + s=text + if s!='': + self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s), + forceNewAction=True) + self.write(text) + self.UpdateUndoHistoryAfter() + + self.ensureSingleGroupingMarker() + self.ensureSingleIOMarker() + + + def clearCommand(self): + """Delete the current, unexecuted command.""" + if not self.CanEdit(): + return + start,end=self.GetIOSlice() + startpos = self.PositionFromLine(start) + endpos = self.GetLineEndPosition(end) + self.SetSelection(startpos, endpos) + self.ReplaceSelection('') + self.more = False + + def OnHistoryReplace(self, step): + """Replace with the previous/next command from the history buffer.""" + if not self.CanEdit(): + return + self.clearCommand() + self.replaceFromHistory(step) + + def replaceFromHistory(self, step): + """Replace selection with command from the history buffer.""" + if not self.CanEdit(): + return + self.ReplaceSelection('') + newindex = self.historyIndex + step + if -1 <= newindex <= len(self.history): + self.historyIndex = newindex + if 0 <= newindex <= len(self.history)-1: + command = self.history[self.historyIndex] + command = command.replace('\n', os.linesep)# + ps2) + self.ReplaceSelection(command) + + def OnHistoryInsert(self, step): + """Insert the previous/next command from the history buffer.""" + if not self.CanEdit(): + return + startpos = self.GetCurrentPos() + self.replaceFromHistory(step) + endpos = self.GetCurrentPos() + self.SetSelection(endpos, startpos) + + # TODO: Fix Me! + def OnHistorySearch(self): + """Search up the history buffer for the text in front of the cursor.""" + if not self.CanEdit(): + return + startpos = self.GetCurrentPos() + # The text up to the cursor is what we search for. + numCharsAfterCursor = self.GetTextLength() - startpos + searchText = self.getCommand(rstrip=False) + #print 'history search', startpos,numCharsAfterCursor,searchText + if numCharsAfterCursor > 0: + searchText = searchText[:-numCharsAfterCursor] + if not searchText: + return + # Search upwards from the current history position and loop + # back to the beginning if we don't find anything. + if (self.historyIndex <= -1) \ + or (self.historyIndex >= len(self.history)-2): + searchOrder = range(len(self.history)) + else: + searchOrder = range(self.historyIndex+1, len(self.history)) + \ + range(self.historyIndex) + for i in searchOrder: + command = self.history[i] + if command[:len(searchText)] == searchText: + # Replace the current selection with the one we found. + self.ReplaceSelection(command[len(searchText):]) + endpos = self.GetCurrentPos() + self.SetSelection(endpos, startpos) + # We've now warped into middle of the history. + self.historyIndex = i + break + + def setStatusText(self, text): + """Display status information.""" + + # This method will likely be replaced by the enclosing app to + # do something more interesting, like write to a status bar. + print text + + def insertLineBreak(self): + """Insert a new line break.""" + if not self.CanEdit(): + return + elif self.reader.isreading: + self.processLine() + return + + + # write with undo wrapper... + cpos=self.GetCurrentPos() + s=os.linesep + self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+1) + self.write(s,type='Input') + self.UpdateUndoHistoryAfter() + + self.more = True + self.prompt() + + def processLine(self): + """Process the line of text at which the user hit Enter or Shift+RETURN.""" + # The user hit ENTER (Shift+RETURN) (Shift+ENTER) and we need to + # decide what to do. They could be sitting on any line in the slices shell. + thepos = self.GetCurrentPos() + cur_line = self.GetCurrentLine() + marker=self.MarkerGet(cur_line) + if marker & INPUT_MASK: + pass + elif marker & OUTPUT_MASK: + return + else: + pass #print 'BLANK LINE!!' + + startline,endline=self.GetIOSlice(cur_line) + + if startline==0: + startpos=0 + else: + startpos=self.PositionFromLine(startline) + + endpos=self.GetLineEndPosition(endline) + + # If they hit ENTER inside the current command, execute the command. + if self.CanEdit(): + self.SetCurrentPos(endpos) + self.interp.more = False + command = self.GetTextRange(startpos, endpos) + lines = command.split(os.linesep) + lines = [line.rstrip() for line in lines] + command = '\n'.join(lines) + if self.reader.isreading: + if not command: + # Match the behavior of the standard Python shell + # when the user hits return without entering a value. + command = '\n' + self.reader.input = command + self.write(os.linesep,'Input') + self.MarkerSet(self.GetCurrentLine(),READLINE_BG) + self.MarkerSet(self.GetCurrentLine(),INPUT_READLINE) + else: + self.runningSlice = (startline,endline) + self.push(command,useMultiCommand=True) + #print 'command: ',command + wx.FutureCall(1, self.EnsureCaretVisible) + self.runningSlice=None + + skip=self.BackspaceWMarkers(force=True) + if skip: + self.DeleteBack() + + if self.GetCurrentLine()==self.GetLineCount()-1: + self.write(os.linesep,type='Input') + cpos=self.GetCurrentLine() + if self.MarkerGet(cpos-1) & OUTPUT_MASK: + self.MarkerAdd(cpos-1,OUTPUT_BG) + self.SplitSlice() + else: + cur_line=self.GetCurrentLine() + new_pos=self.GetLineEndPosition(cur_line+1) + self.SetSelection(new_pos,new_pos) + self.SetCurrentPos(new_pos) + + self.EmptyUndoBuffer() + self.NeedsCheckForSave=True + if self.hasSyntaxError: + pos=self.GetLineEndPosition(self.syntaxErrorRealLine) + self.SetCurrentPos(pos) + self.SetSelection(pos,pos) + + # Not Used!! + def getMultilineCommand(self, rstrip=True): + """Extract a multi-line command from the editor. + + The command may not necessarily be valid Python syntax.""" + # DNM + # XXX Need to extract real prompts here. Need to keep track of + # the prompt every time a command is issued. + text = self.GetCurLine()[0] + line = self.GetCurrentLine() + # Add Marker testing here... + while text == '' and line > 0: # Need to add markers handling... + line -= 1 + self.GotoLine(line) + text = self.GetCurLine()[0] + if text=='': + line = self.GetCurrentLine() + self.GotoLine(line) + startpos = self.GetCurrentPos() + line += 1 + self.GotoLine(line) + while self.GetCurLine()[0]=='': + line += 1 + self.GotoLine(line) + stoppos = self.GetCurrentPos() + command = self.GetTextRange(startpos, stoppos) + command = command.replace(os.linesep, '\n') + command = command.rstrip() + command = command.replace('\n', os.linesep) + else: + command = '' + if rstrip: + command = command.rstrip() + return command + + def getCommand(self, text=None, rstrip=True): + """Extract a command from text which may include a shell prompt. + + The command may not necessarily be valid Python syntax.""" + if not text: + text = self.GetCurLine()[0] + # Strip the prompt off the front leaving just the command. + command = self.lstripPrompt(text) + # Change this -- Nothing has prompts! + #if command == text: + # command = '' # Real commands have prompts. + if rstrip: + command = command.rstrip() + return command + + def lstripPrompt(self, text): + """Return text without a leading prompt.""" + ps1 = str(sys.ps1) + ps1size = len(ps1) + ps2 = str(sys.ps2) + ps2size = len(ps2) + # Strip the prompt off the front of text. + if text[:ps1size] == ps1: + text = text[ps1size:] + elif text[:ps2size] == ps2: + text = text[ps2size:] + return text + + def push(self, command, silent = False,useMultiCommand=False): + """Send command to the interpreter for execution.""" + if not silent: + self.write(os.linesep,type='Output') + # TODO : What other magic might we insert here? + # TODO : Is there a good reason not to include magic? + if USE_MAGIC: + command=magic(command) + + # Allows multi-component commands... + self.hasSyntaxError=False + if useMultiCommand: + result = self.BreakTextIntoCommands(command) + if result[0] == None: + commands=[command] + self.hasSyntaxError=True + syntaxErrorLine=result[1]+1 + self.syntaxErrorRealLine = self.GetCurrentLine()+result[1]-len(command.split('\n')) + else: + commands=result + else: + commands=[command] + + busy = wx.BusyCursor() + self.waiting = True + self.lastUpdate=None + + for i in commands: + if self.hasSyntaxError: + lineno=syntaxErrorLine + offset=0 # not sure how to easily recover this information... + self.write(' File "<input>", line '+str(lineno)+'\n '+i.split('\n')[lineno-1]+'\n'+' '*offset+' ^\nSyntaxError: invalid syntax\n','Error') + else: + self.more = self.interp.push(i+'\n') + # (the \n stops many things from bouncing at the interpreter) + # I could do the following, but I don't really like it! + #if useMultiCommand: + # self.SplitSlice() + self.lastUpdate=None + + if not silent: + self.MarkerAdd(self.GetIOSlice()[0],OUTPUT_BG) + + self.waiting = False + del busy + if not self.more: # could loop-add to history, too, but I don't like it! + self.addHistory(command.rstrip()) + + if not silent: + self.prompt() + + def addHistory(self, command): + """Add command to the command history.""" + # Reset the history position. + self.historyIndex = -1 + # Insert this command into the history, unless it's a blank + # line or the same as the last command. + if command!='' and ( len(self.history)==0 or command!=self.history[0] ): + self.history.insert(0, command) + dispatcher.send(signal="SlicesShell.addHistory", command=command) + + def clearGroupingMarkers(self,line_num=None): + if line_num==None: + line_num=self.GetCurrentLine() + self.MarkerDelete(line_num,GROUPING_START) + self.MarkerDelete(line_num,GROUPING_START_FOLDED) + self.MarkerDelete(line_num,GROUPING_MIDDLE) + self.MarkerDelete(line_num,GROUPING_END) + def clearIOMarkers(self,line_num=None): + if line_num==None: + line_num=self.GetCurrentLine() + self.MarkerDelete(line_num,INPUT_START) + self.MarkerDelete(line_num,INPUT_START_FOLDED) + self.MarkerDelete(line_num,INPUT_MIDDLE) + self.MarkerDelete(line_num,INPUT_END) + self.MarkerDelete(line_num,OUTPUT_START) + self.MarkerDelete(line_num,OUTPUT_START_FOLDED) + self.MarkerDelete(line_num,OUTPUT_MIDDLE) + self.MarkerDelete(line_num,OUTPUT_END) + self.MarkerDelete(line_num,OUTPUT_BG) + self.MarkerDelete(line_num,READLINE_BG) + self.MarkerDelete(line_num,INPUT_READLINE) + def ensureSingleGroupingMarker(self,line_num=None): + if line_num==None: + line_num=self.GetCurrentLine() + marker=self.MarkerGet(line_num) + if marker & 1<<GROUPING_START: + self.MarkerDelete(line_num,GROUPING_START_FOLDED) + self.MarkerDelete(line_num,GROUPING_MIDDLE) + self.MarkerDelete(line_num,GROUPING_END) + elif marker & 1<<GROUPING_START_FOLDED: + self.MarkerDelete(line_num,GROUPING_MIDDLE) + self.MarkerDelete(line_num,GROUPING_END) + elif marker & 1<<GROUPING_MIDDLE: + self.MarkerDelete(line_num,GROUPING_END) + elif marker & 1<<GROUPING_END: + pass + else: + #print 'ERROR! NO GROUPING MARKERS!' + return 1 # Blank marker + + return 0 + + def ensureSingleIOMarker(self,line_num=None): + if line_num==None: + line_num=self.GetCurrentLine() + marker=self.MarkerGet(line_num) + if marker & INPUT_MASK: + self.MarkerDelete(line_num,OUTPUT_START) + self.MarkerDelete(line_num,OUTPUT_START_FOLDED) + self.MarkerDelete(line_num,OUTPUT_MIDDLE) + self.MarkerDelete(line_num,OUTPUT_END) + self.MarkerDelete(line_num,OUTPUT_BG) + [start,start_folded] = [INPUT_START,INPUT_START_FOLDED] + [middle,end] = [INPUT_MIDDLE,INPUT_END] + elif marker & OUTPUT_MASK: + self.MarkerDelete(line_num,INPUT_START) + self.MarkerDelete(line_num,INPUT_START_FOLDED) + self.MarkerDelete(line_num,INPUT_MIDDLE) + self.MarkerDelete(line_num,INPUT_END) + [start,start_folded] = [OUTPUT_START,OUTPUT_START_FOLDED] + [middle,end] = [OUTPUT_MIDDLE,OUTPUT_END] + else: + #print 'ERROR! NO IO MARKERS!' + return 1 # Blank marker + + if marker & 1<<start: + self.MarkerDelete(line_num,start_folded) + self.MarkerDelete(line_num,middle) + self.MarkerDelete(line_num,end) + elif marker & 1<<start_folded: + self.MarkerDelete(line_num,middle) + self.MarkerDelete(line_num,end) + elif marker & 1<<middle: + self.MarkerDelete(line_num,end) + elif marker & 1<<end: + pass + + return 0 + + def RestoreFirstMarker(self): + first_marker=self.MarkerGet(0) + self.clearGroupingMarkers(0) + self.clearIOMarkers(0) + + if first_marker & 1<<GROUPING_START : + self.MarkerAdd(0,GROUPING_START) + elif first_marker & 1<<GROUPING_START_FOLDED : + self.MarkerAdd(0,GROUPING_START_FOLDED) + else: + self.MarkerAdd(0,GROUPING_START) + + if first_marker & 1<<INPUT_START : + self.MarkerAdd(0,INPUT_START) + elif first_marker & 1<<INPUT_START_FOLDED : + self.MarkerAdd(0,INPUT_START_FOLDED) + elif first_marker & 1<<OUTPUT_START : + self.MarkerAdd(0,OUTPUT_START) + #self.MarkerAdd(0,OUTPUT_BG) # More harm than good?? + elif first_marker & 1<<OUTPUT_START_FOLDED : + self.MarkerAdd(0,OUTPUT_START_FOLDED) + #self.MarkerAdd(0,OUTPUT_BG) # More harm than good?? + else: + self.MarkerAdd(0,INPUT_START) + + if self.doHistUpdate: + self.UpdateUndoHistoryAfter() + + def IsAllowedPair(self,m1,m2): + """This testing function ensures that two adjacent markers are valid""" + i_s = 1<<INPUT_START | 1<<INPUT_START_FOLDED + o_s = 1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED + g_s = 1<<GROUPING_START | 1<<GROUPING_START_FOLDED + i_m,o_m,g_m = 1<<INPUT_MIDDLE, 1<<OUTPUT_MIDDLE, 1<<GROUPING_MIDDLE + i_e,o_e,g_e = 1<<INPUT_END, 1<<OUTPUT_END, 1<<GROUPING_END + + if (m1 & i_s) and (m1 & g_s): #1 + if (m2 & i_s) and (m2 & g_s): return True #1 + elif (m2 & i_m) and (m2 & g_m): return True #2 + elif (m2 & i_e) and (m2 & g_m): return True #3 + elif (m2 & i_e) and (m2 & g_e): return True #4 + elif (m2 & o_s) and (m2 & g_s): return False #5 + elif (m2 & o_s) and (m2 & g_m): return True #6 + elif (m2 & o_s) and (m2 & g_e): return True #7 + elif (m2 & o_m) and (m2 & g_m): return False #8 + elif (m2 & o_e) and (m2 & g_e): return False #9 + else: return False + elif (m1 & i_m) and (m1 & g_m): #2 + if (m2 & i_m) and (m2 & g_m): return True #2 + elif (m2 & i_e) and (m2 & g_m): return True #3 + elif (m2 & i_e) and (m2 & g_e): return True #4 + else: return False + elif (m1 & i_e) and (m1 & g_m): #3 + if (m2 & o_s) and (m2 & g_m): return True #6 + elif (m2 & o_s) and (m2 & g_e): return True #7 + else: return False + elif (m1 & i_e) and (m1 & g_e): #4 + if (m2 & i_s) and (m2 & g_s): return True #1 + elif (m2 & o_s) and (m2 & g_s): return True #5 + else: return False + elif (m1 & o_s) and (m1 & g_s): #5 + if (m2 & i_s) and (m2 & g_s): return True #1 + elif (m2 & i_m) and (m2 & g_m): return False #2 + elif (m2 & i_e) and (m2 & g_m): return False #3 + elif (m2 & i_e) and (m2 & g_e): return False #4 + elif (m2 & o_s) and (m2 & g_s): return True #5 + elif (m2 & o_s) and (m2 & g_m): return False #6 + elif (m2 & o_s) and (m2 & g_e): return False #7 + elif (m2 & o_m) and (m2 & g_m): return True #8 + elif (m2 & o_e) and (m2 & g_e): return True #9 + else: return False + elif (m1 & o_s) and (m1 & g_m): #6 + if (m2 & o_m) and (m2 & g_m): return True #8 + elif (m2 & o_e) and (m2 & g_e): return True #9 + else: return False + elif (m1 & o_s) and (m1 & g_e): #7 + if (m2 & i_s) and (m2 & g_s): return True #1 + elif (m2 & o_s) and (m2 & g_s): return True #5 + else: return False + elif (m1 & o_m) and (m1 & g_m): #8 + if (m2 & o_m) and (m2 & g_m): return True #8 + elif (m2 & o_e) and (m2 & g_e): return True #9 + else: return False + elif (m1 & o_e) and (m1 & g_e): #9 + if (m2 & i_s) and (m2 & g_s): return True #1 + elif (m2 & o_s) and (m2 & g_s): return True #5 + else: return False + else: + return False + + + def CleanAllMarkers(self): + self.RestoreFirstMarker() + first_marker=self.MarkerGet(0) + last_line_num=self.GetLineCount()-1 + + for i in range(1,last_line_num): + self.ensureSingleGroupingMarker(i) + self.ensureSingleIOMarker(i) + + previous_marker=self.MarkerGet(i-1) + marker=self.MarkerGet(i) + + if not self.IsAllowedPair(previous_marker,marker): + pass # FIX MARKER!! + # FIX ME + + def write(self, text,type='Input',silent=False): + """Display text in the slices shell. + + Replace line endings with OS-specific endings.""" + text = self.fixLineEndings(text) + split=text.split(os.linesep) + self.AddText(text) + + # This part handles all the marker stuff that accompanies + # adding or removing new lines of text... + # Get the total number of lines in the Document == last line number + last_line_num=self.GetLineCount()-1 + # Get the line number we ended on in the write + end_line_num=self.GetCurrentLine() + # Get the number of returns we are using == number of lines we pasted -1 + num_new_lines=text.count(os.linesep) + # So if num_new_lines==0, start_line_num and end_line_num are the same + start_line_num=end_line_num-num_new_lines+1 + + # This is a little unnecessary because there will always + # be a line before if we just inserted a newline! + if start_line_num == 0: + previous_line_num=None + else: + previous_line_num=start_line_num-1 + + #However, this is very important... + if end_line_num == last_line_num: + next_line_num=None + else: + next_line_num=end_line_num+1 + + if type=='Input': + start = INPUT_START + start_folded = INPUT_START_FOLDED + middle = INPUT_MIDDLE + end = INPUT_END + # preparation for more io types... + opposite_start_mask = 1<<OUTPUT_START + opposite_start_folded_mask = 1<<OUTPUT_START_FOLDED + opposite_middle_mask = 1<<OUTPUT_MIDDLE # To test for bad writes... + opposite_end_mask = 1<<OUTPUT_END # To test for bad writes... + elif type in ['Output','Error']: + #self.MarkerAdd(start_line_num,GROUPING_START_FOLDED) + start=OUTPUT_START + start_folded=OUTPUT_START_FOLDED + middle=OUTPUT_MIDDLE + end=OUTPUT_END + # preparation for more io types... + opposite_start_mask = 1<<INPUT_START + opposite_start_folded_mask = 1<<INPUT_START_FOLDED + opposite_middle_mask = 1<<INPUT_MIDDLE # To test for bad writes... + opposite_end_mask = 1<<INPUT_END # To test for bad writes... + + if num_new_lines>0: #Do nothing if typing within a line... + # Update the Grouping Markers + # For the previous line and the start_line + # Test to make sure we can write ... but not here ... + # test this before we call write or before we add text... + # So we assume it already obeys the rules + + badMarkers=False + fixIOEnd=True + + if previous_line_num==None: + # This is an impossible case, here just for completeness... + self.clearGroupingMarkers(start_line_num) + self.MarkerAdd(start_line_num,GROUPING_START) + + self.clearIOMarkers(start_line_num) + self.MarkerAdd(start_line_num,start) + if type in ['Output','Error']: self.MarkerAdd(start_line_num,OUTPUT_BG) + else: + previous_marker=self.MarkerGet(previous_line_num) + if previous_marker & opposite_middle_mask: + badMarkers=True + + if next_line_num==None: + self.MarkerAdd(end_line_num,GROUPING_END) + self.MarkerAdd(end_line_num,end) + if type in ['Output','Error']: self.MarkerAdd(end_line_num,OUTPUT_BG) + fixEndMarkers=False + # May be overwritten below if start_line_num==end_line_num... + else: + next_marker=self.MarkerGet(next_line_num) + fixEndMarkers=True + if next_marker & ( opposite_middle_mask | opposite_end_mask ): + badMarkers=True + + if not badMarkers: + # ensure previous_line only has one marker & turn end into middle + if previous_line_num!=None: + # Adjust previous line appropriately, ensure only one marker + # Only print errors if we are on input! + blank=False + blank=blank or self.ensureSingleGroupingMarker(previous_line_num) + blank=blank or self.ensureSingleIOMarker(previous_line_num) + + if blank: + #if type=='Input' and not silent: print 'BLANK LINE!' # BAD CASE + pass + + if previous_marker & 1<<GROUPING_END : + # Make GROUPING slice continue unless we hit + # an output end and are starting a new input... + if (previous_marker & OUTPUT_MASK) and type=='Input': + pass + else: + self.MarkerDelete(previous_line_num,GROUPING_END) + # ONLY CHANGING CASE + self.MarkerAdd(previous_line_num,GROUPING_MIDDLE) + + if previous_marker & 1<<end : + self.MarkerDelete(previous_line_num,end) + self.MarkerAdd(previous_line_num,middle) # ONLY CHANGING CASE + if type in ['Output','Error']: self.MarkerAdd(previous_line_num,OUTPUT_BG) + elif previous_marker & opposite_middle_mask : + # BAD CASE + if type=='Input' and not silent: + #print 'Should have been a bad marker!' + pass + + # We can only add input to an input slice + # And can only add output to an output slice + + if previous_marker & ( opposite_start_mask | + opposite_start_folded_mask | + opposite_end_mask ): + if type=='Input': + self.clearGroupingMarkers(start_line_num) + self.MarkerAdd(start_line_num,GROUPING_START) + if start_line_num==end_line_num: + fixEndMarkers=False + else: + if start_line_num==end_line_num: + fixIOEnd=False + self.clearIOMarkers(start_line_num) + self.MarkerAdd(start_line_num,start) + if type in ['Output','Error']: self.MarkerAdd(start_line_num,OUTPUT_BG) + else: + if next_line_num!=None: + self.clearGroupingMarkers(start_line_num) + self.clearIOMarkers(start_line_num) + self.MarkerAdd(start_line_num,GROUPING_MIDDLE) + self.MarkerAdd(start_line_num,middle) + if type in ['Output','Error']: self.MarkerAdd(start_line_num,OUTPUT_BG) + # This may be overwritten if start_line_num==end_line_num + + # Take care of all the middle lines... + # Does nothing for only one line... + for i in range(start_line_num,end_line_num): + self.clearGroupingMarkers(i) + self.MarkerAdd(i,GROUPING_MIDDLE) + + self.clearIOMarkers(i) + self.MarkerAdd(i,middle) + if type in ['Output','Error']: self.MarkerAdd(i,OUTPUT_BG) + + if fixEndMarkers: + # Take care of the end_line if we haven't already done so... + blank=False + blank=blank or self.ensureSingleGroupingMarker(next_line_num) + blank=blank or self.ensureSingleIOMarker(next_line_num) + + if blank: + if type=='Input' and not silent: + #print 'BLANK LINE!' # BAD CASE + pass + + self.clearGroupingMarkers(end_line_num) + if fixIOEnd: + self.clearIOMarkers(end_line_num) + + if next_marker & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) : + self.MarkerAdd(end_line_num,GROUPING_END) + elif next_marker & ( 1<<GROUPING_MIDDLE | 1<<GROUPING_END ) : + self.MarkerAdd(end_line_num,GROUPING_MIDDLE) + + if fixIOEnd: + if next_marker & ( 1<<start | 1<<start_folded ) : + self.MarkerAdd(end_line_num,end) + if type in ['Output','Error']: self.MarkerAdd(end_line_num,OUTPUT_BG) + elif next_marker & ( 1<<middle | 1<<end ) : + self.MarkerAdd(end_line_num,middle) + if type in ['Output','Error']: self.MarkerAdd(end_line_num,OUTPUT_BG) + elif next_marker & ( opposite_start_mask | + opposite_start_folded_mask ): + self.MarkerAdd(end_line_num,end) + if type in ['Output','Error']: self.MarkerAdd(end_line_num,OUTPUT_BG) + else: + self.MarkerAdd(end_line_num,start_folded) + if type in ['Output','Error']: self.MarkerAdd(end_line_num,OUTPUT_BG) + if type=='Input' and not silent: + #print 'BAD MARKERS!' + pass + else: + if type=='Input' and not silent: + #print 'BAD MARKERS!!!' + pass + + self.EnsureCaretVisible() + + if self.waiting: + if self.lastUpdate==None: + self.lastUpdate=time.time() + if time.time()-self.lastUpdate > PRINT_UPDATE_MAX_TIME: + self.Update() + self.lastUpdate=time.time() + + def fixLineEndings(self, text): + """Return text with line endings replaced by OS-specific endings.""" + lines = text.split('\r\n') + for l in range(len(lines)): + chunks = lines[l].split('\r') + for c in range(len(chunks)): + chunks[c] = os.linesep.join(chunks[c].split('\n')) + lines[l] = os.linesep.join(chunks) + text = os.linesep.join(lines) + return text + + def prompt(self): # Autoindent added!!! + """Display proper prompt for the context: ps1, ps2 or ps3. + + If this is a continuation line, autoindent as necessary.""" + # TODO : How much of this can I do away with now without prompts?? + + isreading = self.reader.isreading + + skip = True + if isreading: + prompt = str(sys.ps3) + elif self.more: + prompt = str(sys.ps2) + else: + prompt = str(sys.ps1) + pos = self.GetCurLine()[1] + if pos > 0: + if isreading: + skip = True + else: + self.write(os.linesep,type='Input') + if not self.more: + # Not needed anymore! # self.promptPosStart = self.GetCurrentPos() + pass + if not skip: + self.write(prompt,type='Input') + if not self.more: + # Not needed anymore! # self.promptPosEnd = self.GetCurrentPos() + # Clear the undo history after running a command. + self.EmptyUndoBuffer() + + #DNM/CP + # Autoindent magic + # Match the indent of the line above + # UNLESS the line above ends in a colon...then add four spaces + # (after valid keywords (if, else, etc...) only) + if self.more: + line_num=self.GetCurrentLine() + currentLine=self.GetLine(line_num) + previousLine=self.GetLine(line_num-1) + pstrip=previousLine.strip() + lstrip=previousLine.lstrip() + + if pstrip == '': + # because it is all whitespace! + indent=previousLine.strip('\n').strip('\r') + else: + indent=previousLine[:(len(previousLine)-len(lstrip))] + if testForContinuations(previousLine,ignoreErrors=True)[1][0]: + indent+=' '*4 + + #ADD UNDO + cpos=self.GetCurrentPos() + s=indent + self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s)) + self.write(s,type='Input') + self.UpdateUndoHistoryAfter() + + + self.EnsureCaretVisible() + self.ScrollToColumn(0) + + def readline(self): + """Replacement for stdin.readline().""" + input = '' + reader = self.reader + reader.isreading = True + self.prompt() + + # Ensure that we get a new line and that it's got an input marker... + # Also need to temporarily block any other action... + cLine = self.GetCurrentLine() + self.clearIOMarkers(cLine) + self.MarkerAdd(cLine,INPUT_START) + self.MarkerAdd(cLine,READLINE_BG) + self.MarkerAdd(cLine,INPUT_READLINE) + + try: + while not reader.input: + wx.YieldIfNeeded() + input = reader.input + finally: + start,end = self.GetIOSlice() + start = self.runningSlice[1] + 1 + for i in range(start,end+1): + self.clearIOMarkers(i) + self.clearGroupingMarkers(i) + self.MarkerAdd(i,OUTPUT_BG) + if i == start: self.MarkerAdd(i,OUTPUT_START) + elif i==end: self.MarkerAdd(i,OUTPUT_END) + else: self.MarkerAdd(i,OUTPUT_MIDDLE) + + if i==end: self.MarkerAdd(i,GROUPING_END) + else: self.MarkerAdd(i,GROUPING_MIDDLE) + reader.input = '' + reader.isreading = False + input = str(input) # In case of Unicode. + return input + + def readlines(self): + """Replacement for stdin.readlines().""" + lines = [] + while lines[-1:] != ['\n']: + lines.append(self.readline()) + return lines + + def raw_input(self, prompt=''): + """Return string based on user input.""" + if prompt: + self.write(prompt,type='Output') + return self.readline() + + def ask(self, prompt='Please enter your response:'): + """Get response from the user using a dialog box.""" + dialog = wx.TextEntryDialog(None, prompt, + 'Input Dialog (Raw)', '') + try: + if dialog.ShowModal() == wx.ID_OK: + text = dialog.GetValue() + return text + finally: + dialog.Destroy() + return '' + + def pause(self): + """Halt execution pending a response from the user.""" + self.ask('Press enter to continue:') + + def clear(self): + """Delete all text from the slices shell.""" + self.ClearAll() + self.MarkerAdd(0,GROUPING_START) + self.MarkerAdd(0,INPUT_START) + + def run(self, command, prompt=True, verbose=True): + """Execute command as if it was typed in directly. + >>> shell.run('print "this"') + >>> print "this" + this + >>> + """ + # Go to the very bottom of the text. + endpos = self.GetTextLength() + self.SetCurrentPos(endpos) + command = command.rstrip() + if prompt: self.prompt() + if verbose: self.write(command,type='Input') + self.push(command) + + # TODO : Will have to fix this to handle other kinds of errors mentioned before... + def runfile(self, filename): + """Execute all commands in file as if they were typed into the shell.""" + file = open(filename) + try: + self.prompt() + for command in file.readlines(): + if command[:6] == 'shell.': + # Run shell methods silently. + self.run(command, prompt=False, verbose=False) + else: + self.run(command, prompt=False, verbose=True) + finally: + file.close() + + def autoCompleteShow(self, command, offset = 0): + """Display auto-completion popup list.""" + self.AutoCompSetAutoHide(self.autoCompleteAutoHide) + self.AutoCompSetIgnoreCase(self.autoCompleteCaseInsensitive) + list = self.interp.getAutoCompleteList(command, + includeMagic=self.autoCompleteIncludeMagic, + includeSingle=self.autoCompleteIncludeSingle, + includeDouble=self.autoCompleteIncludeDouble) + if list: + options = ' '.join(list) + #offset = 0 + self.AutoCompShow(offset, options) + + def autoCallTipShow(self, command, insertcalltip = True, forceCallTip = False): + """Display argument spec and docstring in a popup window.""" + if self.CallTipActive(): + self.CallTipCancel() + (name, argspec, tip) = self.interp.getCallTip(command) + if tip: + dispatcher.send(signal='SlicesShell.calltip', sender=self, calltip=tip) + if not self.autoCallTip and not forceCallTip: + return + startpos = self.GetCurrentPos() + if argspec and insertcalltip and self.callTipInsert: + # write with undo history... + cpos=self.GetCurrentPos() + s=argspec + ')' + self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s)) + self.write(s,type='Input') + self.UpdateUndoHistoryAfter() + + endpos = self.GetCurrentPos() + self.SetSelection(startpos, endpos) + if tip: + tippos = startpos - (len(name) + 1) + fallback = startpos - self.GetColumn(startpos) + # In case there isn't enough room, only go back to the fallback. + tippos = max(tippos, fallback) + self.CallTipShow(tippos, tip) + + def OnCallTipAutoCompleteManually (self, shiftDown): + """AutoComplete and Calltips manually.""" + if self.AutoCompActive(): + self.AutoCompCancel() + currpos = self.GetCurrentPos() + stoppos = self.PositionFromLine(self.GetIOSlice()[0]) + + cpos = currpos + #go back until '.' is found + pointavailpos = -1 + while cpos >= stoppos: + if self.GetCharAt(cpos) == ord ('.'): + pointavailpos = cpos + break + cpos -= 1 + + #word from non whitespace until '.' + if pointavailpos != -1: + #look backward for first whitespace char + textbehind = self.GetTextRange (pointavailpos + 1, currpos) + pointavailpos += 1 + + if not shiftDown: + #call AutoComplete + stoppos = self.PositionFromLine(self.GetIOSlice()[0]) + textbefore = self.GetTextRange(stoppos, pointavailpos) + self.autoCompleteShow(textbefore, len (textbehind)) + else: + #call CallTips + cpos = pointavailpos + begpos = -1 + while cpos > stoppos: + if chr(self.GetCharAt(cpos)).isspace(): + begpos = cpos + break + cpos -= 1 + if begpos == -1: + begpos = cpos + ctips = self.GetTextRange (begpos, currpos) + ctindex = ctips.find ('(') + if ctindex != -1 and not self.CallTipActive(): + #insert calltip, if current pos is '(', otherwise show it only + self.autoCallTipShow( ctips[:ctindex + 1], + self.GetCharAt(currpos - 1) == ord('(') and + self.GetCurrentPos() == self.GetTextLength(), + True ) + + + def writeOut(self, text): + """Replacement for stdout.""" + self.write(text,type='Output') + # TODO : FLUSH?? How to make this update real-time... + + def writeErr(self, text): + """Replacement for stderr.""" + self.write(text,type='Error') + + def redirectStdin(self, redirect=True): + """If redirect is true then sys.stdin will come from the shell.""" + if redirect: + sys.stdin = self.reader + else: + sys.stdin = self.stdin + + def redirectStdout(self, redirect=True): + """If redirect is true then sys.stdout will go to the shell.""" + if redirect: + sys.stdout = PseudoFileOut(self.writeOut) + else: + sys.stdout = self.stdout + + def redirectStderr(self, redirect=True): + """If redirect is true then sys.stderr will go to the shell.""" + if redirect: + sys.stderr = PseudoFileErr(self.writeErr) + else: + sys.stderr = self.stderr + + # Take a spashot of the WHOLE grouping slice (or slices) + # The argument s is either what got added or deleted + def UpdateUndoHistoryBefore(self,actionType,s,posStart,posEnd, + forceNewAction=False): + uH=self.undoHistory + uI=self.undoIndex + + s=s.replace(os.linesep,'\n') + startLine=self.LineFromPosition(posStart) + + if actionType=='marker': + numLines = self.LineFromPosition(posEnd) - startLine + else: + numLines=s.count('\n') + + makeNewAction=forceNewAction + + if forceNewAction: + makeNewAction=True + elif self.undoIndex==-1: + makeNewAction=True + elif not uH[uI]['allowsAppend']: + makeNewAction=True + elif actionType!=uH[uI]['actionType']: + makeNewAction=True + elif actionType=='insert': + if posStart!=uH[uI]['posEnd']: + makeNewAction=True + else: # This is a continuation of the previous insert + uH[uI]['charList'] = uH[uI]['charList']+s + uH[uI]['posEnd'] = posEnd # posStart cannot move + uH[uI]['numLines'] = uH[uI]['numLines']+numLines + elif actionType=='delete': + # This is a forward continuation of the previous delete + if posStart==uH[uI]['posStart']: + uH[uI]['charList'] = uH[uI]['charList']+s + uH[uI]['posEnd'] = posEnd + uH[uI]['numLines'] = uH[uI]['numLines']+numLines + # This is a backward continuation of the previous delete + elif posEnd==uH[uI]['posStart']: + uH[uI]['charList'] = s+uH[uI]['charList'] + uH[uI]['posStart'] = posStart + uH[uI]['startLine'] = startLine + uH[uI]['numLines'] = uH[uI]['numLines']+numLines + else: + makeNewAction=True + + elif actionType=='marker': + makeNewAction=True + else: + pass #print 'Unsupported Action Type!!' + + if makeNewAction: + del(self.undoHistory[uI+1:]) # remove actions after undoIndex + + uH.append({ + 'actionType' : actionType, # Action type ('insert','delete','marker') + 'allowsAppend': not forceNewAction, # Can action be joined with others? + 'charList' : s, # Character list + 'posStart' : posStart, # Cursor poition at the start of the action + 'posEnd' : posEnd, # Cursor position at the end of the action + 'startLine' : startLine, # Start line number, + 'numLines' : numLines, # Number of newlines involved + 'mBStart' : None, # Starting line for markers BEFORE action + 'mAStart' : None, # Starting line for markers AFTER action + 'markersBefore' : None, # [markers BEFORE action] + 'markersAfter' : None # [markers AFTER action] + }) + + self.undoIndex+=1 + + # Only update the before when starting a new action + start = startLine + if actionType=='insert': + end = start + else: + end = start + numLines + + # Update Marker Info + newStart=self.GetGroupingSlice(start)[0] + newEnd=self.GetGroupingSlice(end)[1] + self.undoHistory[self.undoIndex]['markersBefore'] = \ + [self.MarkerGet(i) for i in range(newStart,newEnd+1)] + self.undoHistory[self.undoIndex]['mBStart']=newStart + + self.doHistUpdate=True + + def UpdateUndoHistoryAfter(self): # s is either what got added or deleted + start = self.undoHistory[self.undoIndex]['startLine'] + if self.undoHistory[self.undoIndex]['actionType']=='delete': + end = start + else: + end = start + self.undoHistory[self.undoIndex]['numLines'] + + newStart=min(self.GetGroupingSlice(start)[0]-1, 0) + newEnd=max(self.GetGroupingSlice(end)[1]+1, self.GetLineCount()-1) + self.undoHistory[self.undoIndex]['markersAfter'] = \ + [self.MarkerGet(i) for i in range(newStart,newEnd+1)] + self.undoHistory[self.undoIndex]['mAStart']=newStart + + self.doHistUpdate=False + + def Undo(self): + #ADD UNDO + #Skip undo if there are no actions... + if self.undoIndex==-1: + return + + uHI=self.undoHistory[self.undoIndex] + + if uHI['actionType'] in ['insert','delete']: + # This will perform the opposite of the action given + editwindow.EditWindow.Undo(self) + elif uHI['actionType']=='marker': # No text changed, don't pass to STC + pass + else: + #print 'Unsupported actionType in undoHistory!!' + return + + numLines=len(uHI['markersBefore']) + for i in range(numLines): + self.MarkerSet( uHI['mBStart']+i , uHI['markersBefore'][i] ) + + self.undoIndex-=1 + + def Redo(self): + #ADD UNDO + # First check to see if there are any redo operations available + # Note that for redo, undoIndex=-1 is a valid input + if self.undoIndex >= len(self.undoHistory)-1: + return + self.undoIndex+=1 + uHI=self.undoHistory[self.undoIndex] + + if uHI['actionType'] in ['insert','delete']: + # This will re-perform the given action + editwindow.EditWindow.Redo(self) + elif uHI['actionType']=='marker': # No text changed, don't pass to STC + pass + else: + #print 'Unsupported actionType in undoHistory!!' + return + + numLines=len(uHI['markersAfter']) + for i in range(numLines): + self.MarkerSet( uHI['mAStart']+i , uHI['markersAfter'][i] ) + + def EmptyUndoBuffer(self): + editwindow.EditWindow.EmptyUndoBuffer(self) + self.undoIndex=-1 + self.undoHistory=[] + self.doHistUpdate=False + + def CanCut(self): + return self.CanEdit() and \ + (self.GetSelectionStart() != self.GetSelectionEnd()) + + def CanPaste(self): + """Return true if a paste should succeed.""" + if self.CanEdit() and editwindow.EditWindow.CanPaste(self): + return True + else: + return False + + def CanEdit(self): + """Return true if editing should succeed.""" + marker=self.MarkerGet(self.GetCurrentLine()) + + if marker & OUTPUT_MASK: + return False + elif marker & INPUT_MASK: + if self.reader.isreading and not \ + (self.MarkerGet(self.GetCurrentLine()) & 1<<INPUT_READLINE ): + return False + start,end=self.GetIOSlice() + sliceStartPos=self.PositionFromLine(start) + sliceEndPos=self.GetLineEndPosition(end) + """Return true if text is selected and can be cut.""" + if self.GetSelectionStart() == self.GetSelectionEnd(): + return True + elif self.GetSelectionStart() != self.GetSelectionEnd() \ + and self.GetSelectionStart() >= sliceStartPos \ + and self.GetSelectionEnd() >= sliceStartPos \ + and self.GetSelectionStart() <= sliceEndPos \ + and self.GetSelectionEnd() <= sliceEndPos: + return True + else: + return False + + def Cut(self): + """Remove selection and place it on the clipboard.""" + if self.CanCut() and self.CanCopy(): + if self.AutoCompActive(): + self.AutoCompCancel() + if self.CallTipActive(): + self.CallTipCancel() + self.Copy() + self.ReplaceSelection('') + + def Copy(self): + """Copy selection and place it on the clipboard.""" + if self.CanCopy(): + ps1 = str(sys.ps1) + ps2 = str(sys.ps2) + command = self.GetSelectedText() + command = command.replace(os.linesep + ps2, os.linesep) + command = command.replace(os.linesep + ps1, os.linesep) + command = self.lstripPrompt(text=command) + data = wx.TextDataObject(command) + self._clip(data) + + def CopyWithPrompts(self): + """Copy selection, including prompts, and place it on the clipboard.""" + if self.CanCopy(): + command = self.GetSelectedText() + data = wx.TextDataObject(command) + self._clip(data) + + def CopyWithPromptsPrefixed(self): + """Copy selection, including prompts prefixed with four + spaces, and place it on the clipboard.""" + if self.CanCopy(): + command = self.GetSelectedText() + spaces = ' ' * 4 + command = spaces + command.replace(os.linesep, + os.linesep + spaces) + data = wx.TextDataObject(command) + self._clip(data) + + def _clip(self, data): + if wx.TheClipboard.Open(): + wx.TheClipboard.UsePrimarySelection(False) + wx.TheClipboard.SetData(data) + wx.TheClipboard.Flush() + wx.TheClipboard.Close() + + def Paste(self): + """Replace selection with clipboard contents.""" + + #ADD UNDO + if self.CanPaste() and wx.TheClipboard.Open(): + ps2 = str(sys.ps2) + if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)): + data = wx.TextDataObject() + if wx.TheClipboard.GetData(data): + self.ReplaceSelection('') + command = data.GetText() + command = command.rstrip() + command = self.fixLineEndings(command) + command = self.lstripPrompt(text=command) + # TODO : This is still useful... Add it back other places? + command = command.replace(os.linesep + ps2, '\n') + command = command.replace(os.linesep, '\n') + #DNM--Don't use '... ' + command = command.replace('\n', os.linesep)# + ps2) + + cpos=self.GetCurrentPos() + s=command + self.UpdateUndoHistoryBefore('insert', s, cpos, + cpos+len(s), forceNewAction=True) + self.write(s,type='Input') + self.UpdateUndoHistoryAfter() + + # Makes paste -> type -> undo consistent with other STC apps + self.ReplaceSelection('') + wx.TheClipboard.Close() + + + def PasteAndRun(self): + """Replace selection with clipboard contents, run commands.""" + text = '' + if wx.TheClipboard.Open(): + if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)): + data = wx.TextDataObject() + if wx.TheClipboard.GetData(data): + text = data.GetText() + wx.TheClipboard.Close() + if text: + self.Execute(text) + + + def Execute(self, text): + """Replace selection with text and run commands.""" + start,end=self.GetIOSlice() + startpos=self.PositionFromLine(start) + endpos=self.GetLineEndPosition(end) + + self.SetCurrentPos(endpos) + self.SetSelection(startpos, endpos) + self.ReplaceSelection('') + + hasSyntaxError=False + result = self.BreakTextIntoCommands(command) + if result[0] == None: + commands=[command] + hasSyntaxError=True + else: + commands=result + + for command in commands: + command = command.replace('\n', os.linesep) + self.write(command) + self.processLine() + + def wrap(self, wrap=True): + """Sets whether text is word wrapped.""" + try: + self.SetWrapMode(wrap) + except AttributeError: + return 'Wrapping is not available in this version.' + + def zoom(self, points=0): + """Set the zoom level. + + This number of points is added to the size of all fonts. It + may be positive to magnify or negative to reduce.""" + self.SetZoom(points) + + def LoadSettings(self, config): + self.autoComplete = \ + config.ReadBool('Options/AutoComplete', True) + self.autoCompleteIncludeMagic = \ + config.ReadBool('Options/AutoCompleteIncludeMagic', True) + self.autoCompleteIncludeSingle = \ + config.ReadBool('Options/AutoCompleteIncludeSingle', True) + self.autoCompleteIncludeDouble = \ + config.ReadBool('Options/AutoCompleteIncludeDouble', True) + self.autoCallTip = \ + config.ReadBool('Options/AutoCallTip', True) + self.callTipInsert = \ + config.ReadBool('Options/CallTipInsert', True) + + self.SetWrapMode(config.ReadBool('View/WrapMode', True)) + + self.lineNumbers = \ + config.ReadBool('View/ShowLineNumbers', True) + self.setDisplayLineNumbers (self.lineNumbers) + zoom = config.ReadInt('View/Zoom/Shell', -99) + if zoom != -99: + self.SetZoom(zoom) + + + + def SaveSettings(self, config): + config.WriteBool('Options/AutoComplete', self.autoComplete) + config.WriteBool('Options/AutoCompleteIncludeMagic', + self.autoCompleteIncludeMagic) + config.WriteBool('Options/AutoCompleteIncludeSingle', + self.autoCompleteIncludeSingle) + config.WriteBool('Options/AutoCompleteIncludeDouble', + self.autoCompleteIncludeDouble) + config.WriteBool('Options/AutoCallTip', self.autoCallTip) + config.WriteBool('Options/CallTipInsert', self.callTipInsert) + config.WriteBool('View/WrapMode', self.GetWrapMode()) + config.WriteBool('View/ShowLineNumbers', self.lineNumbers) + config.WriteInt('View/Zoom/Shell', self.GetZoom()) + + def GetContextMenu(self): + """ + Create and return a context menu for the slices shell. + This is used instead of the scintilla default menu + in order to correctly respect our immutable buffer. + """ + menu = wx.Menu() + menu.Append(wx.ID_UNDO, "Undo") + menu.Append(wx.ID_REDO, "Redo") + + menu.AppendSeparator() + + menu.Append(wx.ID_CUT, "Cut") + menu.Append(wx.ID_COPY, "Copy") + menu.Append(frame.ID_COPY_PLUS, "Copy Plus") + menu.Append(wx.ID_PASTE, "Paste") + menu.Append(frame.ID_PASTE_PLUS, "Paste Plus") + menu.Append(wx.ID_CLEAR, "Clear") + + menu.AppendSeparator() + + menu.Append(wx.ID_SELECTALL, "Select All") + return menu + + def OnContextMenu(self, evt): + menu = self.GetContextMenu() + self.PopupMenu(menu) + + def OnUpdateUI(self, evt): + id = evt.Id + if id in (wx.ID_CUT, wx.ID_CLEAR): + evt.Enable(self.CanCut()) + elif id in (wx.ID_COPY, frame.ID_COPY_PLUS): + evt.Enable(self.CanCopy()) + elif id in (wx.ID_PASTE, frame.ID_PASTE_PLUS): + evt.Enable(self.CanPaste()) + elif id == wx.ID_UNDO: + evt.Enable(self.CanUndo()) + elif id == wx.ID_REDO: + evt.Enable(self.CanRedo()) + + def LoadPySlicesFile(self,fid): + invalidFileString = 'Not a valid input format' + lineCount=0 + groupingStartLines=[0] + ioStartLines=[0] + ioStartTypes=[] + removeComment=False + + # Read the initial three (or four) lines that have version and marker information + line=fid.readline() + if line == usrBinEnvPythonText: + line=fid.readline() # Add the option to place #!/usr/bin/env python2 at the top + if line not in pyslicesFormatHeaderText: print invalidFileString ; return + line=fid.readline() + if line != groupingStartText: print invalidFileString ; return + line=fid.readline() + if line == inputStartText: ioStartTypes.append('input');removeComment=False + elif line == outputStartText: ioStartTypes.append('output');removeComment=True + else: print invalidFileString ; return + + self.ClearAll() + + # Write the file's text to the text area + # Capture Marker information to + for i in fid: + if i==groupingStartText: + groupingStartLines.append(lineCount) + elif i==inputStartText: + ioStartLines.append(lineCount) + ioStartTypes.append('input') + removeComment=False + elif i==outputStartText: + ioStartLines.append(lineCount) + ioStartTypes.append('output') + removeComment=True + else: + if removeComment: w=i[1:].replace(os.linesep,'\n') + else: w=i.replace(os.linesep,'\n') + self.write(w,'Input',silent=True) + lineCount+=1 + + if w[-1]=='\n': + lineCount+=1 + + for i in range(lineCount+1): + self.clearGroupingMarkers(i) + self.clearIOMarkers(i) + + doMiddle=False + doEnd=False + if groupingStartLines!=[]: + if i == groupingStartLines[0]: + self.MarkerAdd(i,GROUPING_START) + del groupingStartLines[0] + elif i+1 == groupingStartLines[0]: + doEnd=True + else: + doMiddle=True + elif i==lineCount-1: + doEnd=True + else: + doMiddle=True + + if doMiddle: + self.MarkerAdd(i,GROUPING_MIDDLE) + elif doEnd: + self.MarkerAdd(i,GROUPING_END) + + doMiddle=False + doEnd=False + if ioStartLines!=[]: + if i == ioStartLines[0]: + # Delete the old ioStartTypes (keep the current copy for later use) + if i>0: del ioStartTypes[0] + + if ioStartTypes[0]=='input': + self.MarkerAdd(i,INPUT_START) + elif ioStartTypes[0]=='output': + self.MarkerAdd(i,OUTPUT_START) + self.MarkerAdd(i,OUTPUT_BG) + else: + #print 'Invalid Type!'; + return + + # Only delete markers we are totally finished with... + # Keep one more "StartTypes" than "StartLines" + del ioStartLines[0] + elif i+1 == ioStartLines[0]: + doEnd=True + else: + doMiddle=True + elif i==lineCount-1: + doEnd=True + else: + doMiddle=True + + if doMiddle: + if ioStartTypes[0]=='input': + self.MarkerAdd(i,INPUT_MIDDLE) + elif ioStartTypes[0]=='output': + self.MarkerAdd(i,OUTPUT_MIDDLE) + self.MarkerAdd(i,OUTPUT_BG) + else: + #print 'Invalid Type!'; + return + elif doEnd: + if ioStartTypes[0]=='input': + self.MarkerAdd(i,INPUT_END) + elif ioStartTypes[0]=='output': + self.MarkerAdd(i,OUTPUT_END) + self.MarkerAdd(i,OUTPUT_BG) + else: + #print 'Invalid Type!'; + return + + self.EmptyUndoBuffer() # maybe not? + + + def SavePySlicesFile(self,fid): + addComment=False + fid.write(usrBinEnvPythonText.replace('\n',os.linesep)) + fid.write(pyslicesFormatHeaderText[-1].replace('\n',os.linesep)) + for i in range(self.GetLineCount()): + markers=self.MarkerGet(i) + if markers & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ): + fid.write(groupingStartText.replace('\n',os.linesep)) + if markers & ( 1<<INPUT_START | 1<<INPUT_START_FOLDED ): + fid.write(inputStartText.replace('\n',os.linesep)) + addComment=False + if markers & ( 1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED ): + fid.write(outputStartText.replace('\n',os.linesep)) + addComment=True + if addComment: fid.write('#') + fid.write(self.GetLine(i).replace('\n',os.linesep)) + + # FIX ME!! + def LoadPyFileAsSlice(self,fid): + curpos=self.GetCurrentPos() + start,end = self.GetGroupingSlice() + + endpos=self.GetLineEndPosition(end) + self.SetCurrentPos(endpos) + self.SetSelection(endpos, endpos) + + text='\n'+fid.read() + self.write(text,'Input') + newpos=self.GetCurrentPos() + + self.SetCurrentPos(curpos) + self.SetSelection(curpos,curpos) + self.SplitSlice() + #self.SetCurrentPos(newpos) + #self.SetSelection(newpos,newpos) + + def hasChanged(self): + """Return True if contents have changed.""" + return self.GetModify() or self.NeedsCheckForSave + + + +## NOTE: The DnD of file names is disabled until we can figure out how +## best to still allow DnD of text. + +## #seb : File drag and drop +## class FileDropTarget(wx.FileDropTarget): +## def __init__(self, obj): +## wx.FileDropTarget.__init__(self) +## self.obj = obj +## def OnDropFiles(self, x, y, filenames): +## if len(filenames) == 1: +## txt = 'r\"%s\"' % filenames[0] +## else: +## txt = '( ' +## for f in filenames: +## txt += 'r\"%s\" , ' % f +## txt += ')' +## self.obj.AppendText(txt) +## pos = self.obj.GetCurrentPos() +## self.obj.SetCurrentPos( pos ) +## self.obj.SetSelection( pos, pos ) + + + +## class TextAndFileDropTarget(wx.DropTarget): +## def __init__(self, sliceshell): +## wx.DropTarget.__init__(self) +## self.sliceshell = sliceshell +## self.compdo = wx.DataObjectComposite() +## self.textdo = wx.TextDataObject() +## self.filedo = wx.FileDataObject() +## self.compdo.Add(self.textdo) +## self.compdo.Add(self.filedo, True) + +## self.SetDataObject(self.compdo) + +## def OnDrop(self, x, y): +## return True + +## def OnData(self, x, y, result): +## self.GetData() +## if self.textdo.GetTextLength() > 1: +## text = self.textdo.GetText() +## # *** Do somethign with the dragged text here... +## self.textdo.SetText('') +## else: +## filenames = str(self.filename.GetFilenames()) +## if len(filenames) == 1: +## txt = 'r\"%s\"' % filenames[0] +## else: +## txt = '( ' +## for f in filenames: +## txt += 'r\"%s\" , ' % f +## txt += ')' +## self.sliceshell.AppendText(txt) +## pos = self.sliceshell.GetCurrentPos() +## self.sliceshell.SetCurrentPos( pos ) +## self.sliceshell.SetSelection( pos, pos ) + +## return result diff --git a/lib/python2.7/site-packages/wx-3.0-msw/wx/py/version.py b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/version.py new file mode 100644 index 0000000..d47909d --- /dev/null +++ b/lib/python2.7/site-packages/wx-3.0-msw/wx/py/version.py @@ -0,0 +1,9 @@ +"""Provides an object representing the current 'version' or 'release' +of Py as a whole. Individual classes, such as the shell, filling and +interpreter, each have a revision property based on the CVS Revision.""" + +__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +VERSION = '0.9.8' |