~brian-sidebotham/wxwidgets-cmake/wxpython-2.9.4

« back to all changes in this revision

Viewing changes to wxPython/wx/py/sliceshell.py

  • Committer: Brian Sidebotham
  • Date: 2013-08-03 14:30:08 UTC
  • Revision ID: brian.sidebotham@gmail.com-20130803143008-c7806tkych1tp6fc
Initial import into Bazaar

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""Slices is an interactive text control in which a user types in
 
2
commands to be sent to the interpreter.  This particular shell is
 
3
based on wxPython's wxStyledTextCtrl.
 
4
 
 
5
Sponsored by Orbtech - Your source for Python programming expertise.
 
6
Slices is a version of shell modified by David Mashburn."""
 
7
 
 
8
__author__ = "David N. Mashburn <david.n.mashburn@gmail.com> / "
 
9
__author__ += "Patrick K. O'Brien <pobrien@orbtech.com>"
 
10
__cvsid__ = "$Id: sliceshell.py 60100 2009-04-12 02:56:29Z RD $"
 
11
__revision__ = "$Revision: 60100 $"[11:-2]
 
12
 
 
13
import wx
 
14
from wx import stc
 
15
 
 
16
import keyword
 
17
import os
 
18
import sys
 
19
import time
 
20
 
 
21
from buffer import Buffer
 
22
import dispatcher
 
23
import editor
 
24
import editwindow
 
25
import document
 
26
import frame
 
27
from pseudo import PseudoFileIn
 
28
from pseudo import PseudoFileOut
 
29
from pseudo import PseudoFileErr
 
30
from version import VERSION
 
31
from magic import magic
 
32
from parse import testForContinuations
 
33
from path import ls,cd,pwd,sx
 
34
 
 
35
 
 
36
sys.ps3 = '<-- '  # Input prompt.
 
37
USE_MAGIC=True
 
38
# Force updates from long-running commands after this many seconds
 
39
PRINT_UPDATE_MAX_TIME=2
 
40
 
 
41
NAVKEYS = (wx.WXK_HOME, wx.WXK_END, wx.WXK_LEFT, wx.WXK_RIGHT,
 
42
           wx.WXK_UP, wx.WXK_DOWN, wx.WXK_PRIOR, wx.WXK_NEXT)
 
43
 
 
44
GROUPING_SELECTING=0
 
45
IO_SELECTING = 1
 
46
 
 
47
GROUPING_START = 2
 
48
GROUPING_START_FOLDED = 3
 
49
GROUPING_MIDDLE = 4
 
50
GROUPING_END = 5
 
51
INPUT_START = 6
 
52
INPUT_START_FOLDED = 7
 
53
INPUT_MIDDLE = 8
 
54
INPUT_END = 9
 
55
OUTPUT_START = 10
 
56
OUTPUT_START_FOLDED = 11
 
57
OUTPUT_MIDDLE = 12
 
58
OUTPUT_END = 13
 
59
 
 
60
OUTPUT_BG = 14
 
61
READLINE_BG = 15
 
62
INPUT_READLINE = 16
 
63
 
 
64
# Could add C integration right into the markers...
 
65
# Non-editable file marker for auto-loaded files...
 
66
# Weave VariableInput = 15
 
67
# Weave C code = 16
 
68
# C code = 17 (only for use with Pyrex)
 
69
# Pyrex / Cython code = 18
 
70
 
 
71
GROUPING_MASK = ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED |
 
72
                  1<<GROUPING_MIDDLE | 1<<GROUPING_END )
 
73
 
 
74
INPUT_MASK = ( 1<<INPUT_START | 1<<INPUT_START_FOLDED |
 
75
               1<<INPUT_MIDDLE | 1<<INPUT_END )
 
76
OUTPUT_MASK = ( 1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED |
 
77
                1<<OUTPUT_MIDDLE | 1<<OUTPUT_END )
 
78
IO_MASK = ( INPUT_MASK | OUTPUT_MASK )
 
79
 
 
80
IO_START_MASK = ( 1<<INPUT_START | 1<<OUTPUT_START )
 
81
IO_START_FOLDED_MASK = ( 1<<INPUT_START_FOLDED | 1<<OUTPUT_START_FOLDED )
 
82
IO_ANY_START_MASK = ( 1<<INPUT_START | 1<<OUTPUT_START |
 
83
                      1<<INPUT_START_FOLDED | 1<<OUTPUT_START_FOLDED )
 
84
IO_MIDDLE_MASK = ( 1<<INPUT_MIDDLE | 1<<OUTPUT_MIDDLE )
 
85
IO_END_MASK = ( 1<<INPUT_END | 1<<OUTPUT_END )
 
86
 
 
87
usrBinEnvPythonText = '#!/usr/bin/env python\n'
 
88
pyslicesFormatHeaderText = ['#PySlices Save Format Version 1.1 (PySlices v0.9.7.8 and later)\n',
 
89
                            '#PySlices Save Format Version 1.2 (PySlices v0.9.8 and later)\n']
 
90
groupingStartText = '#PySlices Marker Information -- Begin Grouping Slice\n'
 
91
inputStartText = '#PySlices Marker Information -- Begin Input Slice\n'
 
92
outputStartText = '#PySlices Marker Information -- Begin Output Slice\n'
 
93
 
 
94
tutorialText = """
 
95
 
 
96
                            Tutorial!!!
 
97
------------------------------------------------------------------------
 
98
PySlices is the newest member of the Py suite!
 
99
It is a modified version of PyCrust that supports multi-line commands.
 
100
 
 
101
Input and output are contained in "Slices" shown as markers in the left margin.
 
102
Input Slices have RED margins (active, editable).
 
103
Output Slices have BLUE margins (frozen, not editable).
 
104
 
 
105
Commands in slices can be on more than one line, as with Sage or Mathematica.
 
106
For example, the command:
 
107
a=1
 
108
b=2
 
109
print a+b
 
110
will all run in sequence, much like a script.
 
111
Try running the above Input Slice by clicking somewhere in its text and
 
112
using Ctrl-Return, Shift-Return, or Numpad Enter to execute.
 
113
Previous commands (Old Slices) can be re-edited and run again in place.
 
114
 
 
115
Slices can also be:
 
116
 * selceted (click on the margin, Shift-click for multiple selection)
 
117
 * folded (click the margin twice)
 
118
 * selected and deleted (hit delete while selected)
 
119
 * divided (Ctrl-D)
 
120
 * merged (Ctrl-M while selecting adjacent, like-colored slices)
 
121
 
 
122
Try deleting the slice above this one by clicking on the red margin.
 
123
 
 
124
If you want a more traditional shell feel, try enabling "Shell Mode" in
 
125
"Options->Settings->Shell Mode" (or try PyCrust).
 
126
In Shell Mode, two returns in a row executes the command, and
 
127
    Ctrl-Return and Shift-Return always print newlines.
 
128
 
 
129
Saving and opening "sessions" is now supported!  This is a little
 
130
different to other shells where the history is saved.  With PySlices, 
 
131
the whole document is saved in a simple text format!
 
132
 
 
133
To disable this Tutorial on startup, uncheck it in the menu at:
 
134
"Options->Startup->Show PySlices tutorial"
 
135
 
 
136
PySlices may not be the best thing since sliced bread, but
 
137
I hope it makes using Python a little bit sweeter!
 
138
"""
 
139
 
 
140
class SlicesShellFrame(frame.Frame, frame.ShellFrameMixin):
 
141
    """Frame containing the sliceshell component."""
 
142
 
 
143
    name = 'SlicesShell Frame'
 
144
    revision = __revision__
 
145
 
 
146
    def __init__(self, parent=None, id=-1, title='PySlicesShell',
 
147
                 pos=wx.DefaultPosition, size=wx.DefaultSize,
 
148
                 style=wx.DEFAULT_FRAME_STYLE, locals=None,
 
149
                 InterpClass=None,
 
150
                 config=None, dataDir=None, filename=None,
 
151
                 *args, **kwds):
 
152
        """Create SlicesShellFrame instance."""
 
153
        frame.Frame.__init__(self, parent, id, title, pos, size, style,shellName='PySlices')
 
154
        frame.ShellFrameMixin.__init__(self, config, dataDir)
 
155
        
 
156
        if size == wx.DefaultSize:
 
157
            self.SetSize((750, 525))
 
158
        
 
159
        intro = 'PySlices %s - The Flakiest Python Shell... Cut Up!' % VERSION
 
160
        self.SetStatusText(intro.replace('\n', ', '))
 
161
        self.sliceshell = SlicesShell(parent=self, id=-1, introText=intro,
 
162
                               locals=locals, InterpClass=InterpClass,
 
163
                               startupScript=self.startupScript,
 
164
                               execStartupScript=self.execStartupScript,
 
165
                               showPySlicesTutorial=self.showPySlicesTutorial,
 
166
                               enableShellMode=self.enableShellMode,
 
167
                               hideFoldingMargin=self.hideFoldingMargin,
 
168
                               *args, **kwds)
 
169
        self.buffer = self.sliceshell.buffer
 
170
 
 
171
        # Override the shell so that status messages go to the status bar.
 
172
        self.sliceshell.setStatusText = self.SetStatusText
 
173
 
 
174
        self.sliceshell.SetFocus()
 
175
        self.LoadSettings()
 
176
        
 
177
        self.currentDirectory = os.path.expanduser('~')
 
178
        
 
179
        if filename!=None:
 
180
            self.bufferOpen(filename)
 
181
        
 
182
        self.Bind(wx.EVT_IDLE, self.OnIdle)
 
183
 
 
184
 
 
185
    def OnClose(self, event):
 
186
        """Event handler for closing."""
 
187
        self.bufferClose()
 
188
        # This isn't working the way I want, but I'll leave it for now.
 
189
        #if self.sliceshell.waiting:
 
190
        #    if event.CanVeto():
 
191
        #        event.Veto(True)
 
192
        #else:
 
193
        #    # TODO: Add check for saving
 
194
        #    self.SaveSettings()
 
195
        #    self.sliceshell.destroy()
 
196
        #    self.Destroy()
 
197
 
 
198
    def OnAbout(self, event):
 
199
        """Display an About window."""
 
200
        title = 'About PySliceShell'
 
201
        text = 'PySliceShell %s\n\n' % VERSION + \
 
202
               'Yet another Python shell, only flakier.\n\n' + \
 
203
               'Half-baked by Patrick K. O\'Brien,\n' + \
 
204
               'the other half is still in the oven.\n\n' + \
 
205
               'Shell Revision: %s\n' % self.shell.revision + \
 
206
               'Interpreter Revision: %s\n\n' % self.shell.interp.revision + \
 
207
               'Platform: %s\n' % sys.platform + \
 
208
               'Python Version: %s\n' % sys.version.split()[0] + \
 
209
               'wxPython Version: %s\n' % wx.VERSION_STRING + \
 
210
               ('\t(%s)\n' % ", ".join(wx.PlatformInfo[1:])) 
 
211
        dialog = wx.MessageDialog(self, text, title,
 
212
                                  wx.OK | wx.ICON_INFORMATION)
 
213
        dialog.ShowModal()
 
214
        dialog.Destroy()
 
215
 
 
216
 
 
217
    def OnHelp(self, event):
 
218
        """Show a help dialog."""
 
219
        frame.ShellFrameMixin.OnHelp(self, event)
 
220
 
 
221
 
 
222
    def LoadSettings(self):
 
223
        if self.config is not None:
 
224
            frame.ShellFrameMixin.LoadSettings(self)
 
225
            frame.Frame.LoadSettings(self, self.config)
 
226
            self.sliceshell.LoadSettings(self.config)
 
227
 
 
228
    def SaveSettings(self, force=False):
 
229
        if self.config is not None:
 
230
            frame.ShellFrameMixin.SaveSettings(self,force)
 
231
            if self.autoSaveSettings or force:
 
232
                frame.Frame.SaveSettings(self, self.config)
 
233
                self.sliceshell.SaveSettings(self.config)
 
234
 
 
235
    def DoSaveSettings(self):
 
236
        if self.config is not None:
 
237
            self.SaveSettings(force=True)
 
238
            self.config.Flush()
 
239
    
 
240
    def OnEnableShellMode(self,event):
 
241
        """Change between Slices Mode and Shell Mode"""
 
242
        frame.Frame.OnEnableShellMode(self,event)
 
243
        self.sliceshell.ToggleShellMode(self.enableShellMode)
 
244
    
 
245
    def OnHideFoldingMargin(self,event):
 
246
        """Change between Slices Mode and Shell Mode"""
 
247
        frame.Frame.OnHideFoldingMargin(self,event)
 
248
        self.sliceshell.ToggleFoldingMargin(self.hideFoldingMargin)
 
249
    # Copied Straight from crustslices.py (update both with any changes...)
 
250
    # Stolen Straight from editor.EditorFrame
 
251
    # Modified a little... :)
 
252
    # ||
 
253
    # \/
 
254
    def OnIdle(self, event):
 
255
        """Event handler for idle time."""
 
256
        self._updateTitle()
 
257
        event.Skip()
 
258
    
 
259
    def _updateTitle(self):
 
260
        """Show current title information."""
 
261
        title = self.GetTitle()
 
262
        if self.bufferHasChanged():
 
263
            if title.startswith('* '):
 
264
                pass
 
265
            else:
 
266
                self.SetTitle('* ' + title)
 
267
        else:
 
268
            if title.startswith('* '):
 
269
                self.SetTitle(title[2:])
 
270
    
 
271
    def hasBuffer(self):
 
272
        """Return True if there is a current buffer."""
 
273
        if self.buffer:
 
274
            return True
 
275
        else:
 
276
            return False
 
277
 
 
278
    def bufferClose(self):
 
279
        """Close buffer."""
 
280
        if self.buffer.hasChanged():
 
281
            cancel = self.bufferSuggestSave()
 
282
            if cancel:
 
283
                #event.Veto()
 
284
                return cancel
 
285
        self.SaveSettings()
 
286
        self.sliceshell.destroy()
 
287
        self.bufferDestroy()
 
288
        self.Destroy()
 
289
        
 
290
        return False
 
291
 
 
292
    def bufferCreate(self, filename=None):
 
293
        """Create new buffer."""
 
294
        self.bufferDestroy()
 
295
        buffer = Buffer()
 
296
        self.panel = panel = wx.Panel(parent=self, id=-1)
 
297
        panel.Bind (wx.EVT_ERASE_BACKGROUND, lambda x: x)        
 
298
        editor = Editor(parent=panel)
 
299
        panel.editor = editor
 
300
        sizer = wx.BoxSizer(wx.VERTICAL)
 
301
        sizer.Add(editor.window, 1, wx.EXPAND)
 
302
        panel.SetSizer(sizer)
 
303
        panel.SetAutoLayout(True)
 
304
        sizer.Layout()
 
305
        buffer.addEditor(editor)
 
306
        buffer.open(filename)
 
307
        self.setEditor(editor)
 
308
        self.editor.setFocus()
 
309
        self.SendSizeEvent()
 
310
        
 
311
 
 
312
    def bufferDestroy(self):
 
313
        """Destroy the current buffer."""
 
314
        if self.buffer:
 
315
            self.editor = None
 
316
            self.buffer = None
 
317
 
 
318
 
 
319
    def bufferHasChanged(self):
 
320
        """Return True if buffer has changed since last save."""
 
321
        if self.buffer:
 
322
            return self.buffer.hasChanged()
 
323
        else:
 
324
            return False
 
325
 
 
326
    def bufferNew(self):
 
327
        """Create new buffer."""
 
328
        cancel = self.bufferSuggestSave()
 
329
        if cancel:
 
330
            return cancel
 
331
        #self.bufferCreate()
 
332
        self.clear()
 
333
        self.SetTitle( 'PySlices')
 
334
        self.sliceshell.NeedsCheckForSave=False
 
335
        self.sliceshell.SetSavePoint()
 
336
        self.buffer.doc = document.Document()
 
337
        self.buffer.name = 'This shell'
 
338
        self.buffer.modulename = self.buffer.doc.filebase
 
339
        cancel = False
 
340
        return cancel
 
341
 
 
342
    def bufferOpen(self,file=None):
 
343
        """Open file in buffer."""
 
344
        if self.bufferHasChanged():
 
345
            cancel = self.bufferSuggestSave()
 
346
            if cancel:
 
347
                return cancel
 
348
        
 
349
        if file==None:
 
350
            file=wx.FileSelector('Open a PySlices File',
 
351
                                 wildcard='*.pyslices',
 
352
                                 default_path=self.currentDirectory)
 
353
        if file!=None and file!=u'':
 
354
            fid=open(file,'r')
 
355
            self.sliceshell.LoadPySlicesFile(fid)
 
356
            fid.close()
 
357
            self.currentDirectory = os.path.split(file)[0]
 
358
            self.SetTitle( os.path.split(file)[1] + ' - PySlices')
 
359
            self.sliceshell.NeedsCheckForSave=False
 
360
            self.sliceshell.SetSavePoint()
 
361
            self.buffer.doc = document.Document(file)
 
362
            self.buffer.name = self.buffer.doc.filename
 
363
            self.buffer.modulename = self.buffer.doc.filebase
 
364
            self.sliceshell.ScrollToLine(0)
 
365
        return
 
366
    
 
367
##     def bufferPrint(self):
 
368
##         """Print buffer."""
 
369
##         pass
 
370
 
 
371
##     def bufferRevert(self):
 
372
##         """Revert buffer to version of file on disk."""
 
373
##         pass
 
374
    
 
375
    # was self.buffer.save(self): # """Save buffer."""
 
376
    def simpleSave(self,confirmed=False):
 
377
        filepath = self.buffer.doc.filepath
 
378
        self.buffer.confirmed = confirmed
 
379
        if not filepath:
 
380
            return  # XXX Get filename
 
381
        if not os.path.exists(filepath):
 
382
            self.buffer.confirmed = True
 
383
        if not self.buffer.confirmed:
 
384
            self.buffer.confirmed = self.buffer.overwriteConfirm(filepath)
 
385
        if self.buffer.confirmed:
 
386
            try:
 
387
                fid = open(filepath, 'wb')
 
388
                self.sliceshell.SavePySlicesFile(fid)
 
389
            finally:
 
390
                if fid:
 
391
                    fid.close()
 
392
            self.sliceshell.SetSavePoint()
 
393
            self.SetTitle( os.path.split(filepath)[1] + ' - PySlices')
 
394
            self.sliceshell.NeedsCheckForSave=False
 
395
    
 
396
    def bufferSave(self):
 
397
        """Save buffer to its file."""
 
398
        if self.buffer.doc.filepath:
 
399
            # self.buffer.save()
 
400
            self.simpleSave(confirmed=True)
 
401
            cancel = False
 
402
        else:
 
403
            cancel = self.bufferSaveAs()
 
404
        return cancel
 
405
 
 
406
    def bufferSaveAs(self):
 
407
        """Save buffer to a new filename."""
 
408
        if self.bufferHasChanged() and self.buffer.doc.filepath:
 
409
            cancel = self.bufferSuggestSave()
 
410
            if cancel:
 
411
                return cancel
 
412
        filedir = ''
 
413
        if self.buffer and self.buffer.doc.filedir:
 
414
            filedir = self.buffer.doc.filedir
 
415
        result = editor.saveSingle(title='Save PySlices File',directory=filedir,
 
416
                                   wildcard='PySlices Files (*.pyslices)|*.pyslices')
 
417
        if result.path not in ['',None]:
 
418
            if result.path[-9:]!=".pyslices":
 
419
                result.path+=".pyslices"
 
420
            
 
421
            self.buffer.doc = document.Document(result.path)
 
422
            self.buffer.name = self.buffer.doc.filename
 
423
            self.buffer.modulename = self.buffer.doc.filebase
 
424
            self.simpleSave(confirmed=True) # allow overwrite
 
425
            cancel = False
 
426
        else:
 
427
            cancel = True
 
428
        return cancel
 
429
    
 
430
    def bufferSaveACopy(self):
 
431
        """Save buffer to a new filename."""
 
432
        filedir = ''
 
433
        if self.buffer and self.buffer.doc.filedir:
 
434
            filedir = self.buffer.doc.filedir
 
435
        result = editor.saveSingle(title='Save a Copy of PySlices File',directory=filedir,
 
436
                                   wildcard='PySlices Files (*.pyslices)|*.pyslices')
 
437
        
 
438
        if result.path not in ['',None]:
 
439
            if result.path[-9:]!=".pyslices":
 
440
                result.path+=".pyslices"
 
441
            
 
442
            # if not os.path.exists(result.path):
 
443
            try: # Allow overwrite...
 
444
                fid = open(result.path, 'wb')
 
445
                self.sliceshell.SavePySlicesFile(fid)
 
446
            finally:
 
447
                if fid:
 
448
                    fid.close()
 
449
                
 
450
            cancel = False
 
451
        else:
 
452
            cancel = True
 
453
        return cancel
 
454
    
 
455
    def bufferSuggestSave(self):
 
456
        """Suggest saving changes.  Return True if user selected Cancel."""
 
457
        result = editor.messageDialog(parent=None,
 
458
                               message='%s has changed.\n'
 
459
                                       'Would you like to save it first'
 
460
                                       '?' % self.buffer.name,
 
461
                               title='Save current file?',
 
462
                               style=wx.YES_NO | wx.CANCEL | wx.NO_DEFAULT |
 
463
                                     wx.CENTRE | wx.ICON_QUESTION )
 
464
        if result.positive:
 
465
            cancel = self.bufferSave()
 
466
        else:
 
467
            cancel = result.text == 'Cancel'
 
468
        return cancel
 
469
 
 
470
    def updateNamespace(self):
 
471
        """Update the buffer namespace for autocompletion and calltips."""
 
472
        if self.buffer.updateNamespace():
 
473
            self.SetStatusText('Namespace updated')
 
474
        else:
 
475
            self.SetStatusText('Error executing, unable to update namespace')
 
476
 
 
477
 
 
478
 
 
479
# TODO : Update the help text
 
480
HELP_TEXT = """\
 
481
* Key bindings:
 
482
Home              Go to the beginning of the line.
 
483
End               Go to the end of the line.
 
484
Shift+Home        Select to the beginning of the line.
 
485
Shift+End         Select to the end of the line.
 
486
Ctrl-Home         Jump to the beginning of the slice;
 
487
                  If already there, jump to beginning of previous slice
 
488
Ctrl-End          Jump to the end of the slice;
 
489
                  If already there, jump to end of next slice
 
490
Ctrl-PageUp       Jump to the beginning of the shell
 
491
Ctrl-PageDown     Jump to the end of the shell
 
492
Ctrl+C            Copy selected text, removing prompts.
 
493
Ctrl+Shift+C      Copy selected text, retaining prompts.
 
494
Alt+C             Copy to the clipboard, including prefixed prompts.
 
495
Ctrl+X            Cut selected text.
 
496
Ctrl+V            Paste from clipboard.
 
497
Ctrl+Shift+V      Paste and run multiple commands from clipboard.
 
498
Ctrl+Up Arrow     Retrieve Previous History item.
 
499
Alt+P             Retrieve Previous History item.
 
500
Ctrl+Down Arrow   Retrieve Next History item.
 
501
Alt+N             Retrieve Next History item.
 
502
Shift+Up Arrow    Insert Previous History item.
 
503
Shift+Down Arrow  Insert Next History item.
 
504
F8                Command-completion of History item.
 
505
                  (Type a few characters of a previous command and press F8.)
 
506
Ctrl+]            Increase font size.
 
507
Ctrl+[            Decrease font size.
 
508
Ctrl+=            Default font size.
 
509
 
 
510
Ctrl-Space        Show Auto Completion.
 
511
Ctrl-Shift-Space  Show Call Tip.
 
512
Ctrl-Shift-H      Complete Text from History.
 
513
 
 
514
Ctrl+F            Search 
 
515
Ctrl+G            Search next
 
516
F12               on/off "free-edit" mode
 
517
                  For testing only -- This does not preserve markers!
 
518
 
 
519
In "Slices Mode":
 
520
Return            Insert new line
 
521
Enter (Numpad)    Run command in slice
 
522
Ctrl+Return       ""
 
523
Shift+Return      ""
 
524
 
 
525
In "Shell Mode":
 
526
Return or Enter   Insert a new line
 
527
Ctrl+Return       ""
 
528
Shift+Return      ""
 
529
2 Returns in a row   Run command in slice
 
530
"""
 
531
 
 
532
class SlicesShellFacade:
 
533
    """Simplified interface to all shell-related functionality.
 
534
 
 
535
    This is a semi-transparent facade, in that all attributes of other
 
536
    are accessible, even though only some are visible to the user."""
 
537
 
 
538
    name = 'SlicesShell Interface'
 
539
    revision = __revision__
 
540
 
 
541
    def __init__(self, other):
 
542
        """Create a SlicesShellFacade instance."""
 
543
        d = self.__dict__
 
544
        d['other'] = other
 
545
        d['helpText'] = HELP_TEXT
 
546
        d['this'] = other.this
 
547
 
 
548
    def help(self):
 
549
        """Display some useful information about how to use the slices shell."""
 
550
        self.write(self.helpText,type='Output')
 
551
 
 
552
    def __getattr__(self, name):
 
553
        if hasattr(self.other, name):
 
554
            return getattr(self.other, name)
 
555
        else:
 
556
            raise AttributeError, name
 
557
 
 
558
    def __setattr__(self, name, value):
 
559
        if self.__dict__.has_key(name):
 
560
            self.__dict__[name] = value
 
561
        elif hasattr(self.other, name):
 
562
            setattr(self.other, name, value)
 
563
        else:
 
564
            raise AttributeError, name
 
565
 
 
566
    def _getAttributeNames(self):
 
567
        """Return list of magic attributes to extend introspection."""
 
568
        list = [
 
569
            'about',
 
570
            'ask',
 
571
            'autoCallTip',
 
572
            'autoComplete',
 
573
            'autoCompleteAutoHide',
 
574
            'autoCompleteCaseInsensitive',
 
575
            'autoCompleteIncludeDouble',
 
576
            'autoCompleteIncludeMagic',
 
577
            'autoCompleteIncludeSingle',
 
578
            'callTipInsert',
 
579
            'clear',
 
580
            'pause',
 
581
            'prompt',
 
582
            'quit',
 
583
            'redirectStderr',
 
584
            'redirectStdin',
 
585
            'redirectStdout',
 
586
            'run',
 
587
            'runfile',
 
588
            'wrap',
 
589
            'zoom',
 
590
            ]
 
591
        list.sort()
 
592
        return list
 
593
 
 
594
DISPLAY_TEXT="""
 
595
Author: %r
 
596
Py Version: %s
 
597
Py Slices Shell Revision: %s
 
598
Py Interpreter Revision: %s
 
599
Python Version: %s
 
600
wxPython Version: %s
 
601
wxPython PlatformInfo: %s
 
602
Platform: %s"""
 
603
 
 
604
class SlicesShell(editwindow.EditWindow):
 
605
    """Notebook Shell based on StyledTextCtrl."""
 
606
 
 
607
    name = 'SlicesShell'
 
608
    revision = __revision__
 
609
 
 
610
    def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
 
611
                 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
 
612
                 introText='', locals=None, InterpClass=None,
 
613
                 startupScript=None, execStartupScript=True,
 
614
                 showPySlicesTutorial=True,enableShellMode=False,
 
615
                 hideFoldingMargin=False, *args, **kwds):
 
616
        """Create Shell instance."""
 
617
        editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
 
618
        self.wrap()
 
619
        if locals is None:
 
620
            import __main__
 
621
            locals = __main__.__dict__
 
622
 
 
623
        # Grab these so they can be restored by self.redirect* methods.
 
624
        self.stdin = sys.stdin
 
625
        self.stdout = sys.stdout
 
626
        self.stderr = sys.stderr
 
627
 
 
628
        # Import a default interpreter class if one isn't provided.
 
629
        if InterpClass == None:
 
630
            from interpreter import Interpreter
 
631
        else:
 
632
            Interpreter = InterpClass
 
633
 
 
634
        # Create a replacement for stdin.
 
635
        self.reader = PseudoFileIn(self.readline, self.readlines)
 
636
        self.reader.input = ''
 
637
        self.reader.isreading = False
 
638
 
 
639
        # Set up the interpreter.
 
640
        self.interp = Interpreter(locals=locals,
 
641
                                  rawin=self.raw_input,
 
642
                                  stdin=self.reader,
 
643
                                  stdout=PseudoFileOut(self.writeOut),
 
644
                                  stderr=PseudoFileErr(self.writeErr),
 
645
                                  *args, **kwds)
 
646
 
 
647
        # Set up the buffer.
 
648
        self.buffer = Buffer()
 
649
        self.id = self.GetId()
 
650
        self.buffer.addEditor(self)
 
651
        self.buffer.name='This shell'
 
652
        self.NeedsCheckForSave=False
 
653
        
 
654
        # Find out for which keycodes the interpreter will autocomplete.
 
655
        self.autoCompleteKeys = self.interp.getAutoCompleteKeys()
 
656
 
 
657
        # Keep track of the last non-continuation prompt positions.
 
658
        # Removed all references to these... solved a lot of odd bugs...
 
659
        # self.promptPosStart = 0
 
660
        # self.promptPosEnd = 0
 
661
 
 
662
        # Keep track of multi-line commands.
 
663
        self.more = False
 
664
        
 
665
        # Use Margins to track input / output / slice number
 
666
        self.margins = True
 
667
        
 
668
        # For use with forced updates during long-running scripts
 
669
        self.lastUpdate=None
 
670
        
 
671
        if self.margins:
 
672
            # margin 1 is already defined for the line numbers
 
673
            #  may eventually change it back to 0 like it ought to be...
 
674
            self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
 
675
            self.SetMarginType(3, stc.STC_MARGIN_SYMBOL)
 
676
            self.SetMarginType(4, stc.STC_MARGIN_SYMBOL)
 
677
            self.SetMarginWidth(2, 22)
 
678
            self.SetMarginWidth(3, 22)
 
679
            self.SetMarginWidth(4, 12)
 
680
            self.SetMarginSensitive(2,True)
 
681
            self.SetMarginSensitive(3,True)
 
682
            self.SetMarginSensitive(4,True)
 
683
            self.SetProperty("fold", "1")
 
684
            # tabs are bad, use spaces
 
685
            self.SetProperty("tab.timmy.whinge.level", "4")
 
686
            self.SetMargins(0,0)
 
687
            
 
688
            
 
689
            self.SetMarginMask(2, GROUPING_MASK | 1<<GROUPING_SELECTING )
 
690
            # Display Markers -24...
 
691
            self.SetMarginMask(3, IO_MASK | 1<<IO_SELECTING | 1<<READLINE_BG | 1<<INPUT_READLINE )
 
692
            self.SetMarginMask(4, stc.STC_MASK_FOLDERS)
 
693
            # Set the mask for the line markers, too...
 
694
            self.SetMarginMask(1, 0)
 
695
            
 
696
            if hideFoldingMargin:
 
697
                self.SetMarginWidth(4, 0)
 
698
            self.hideFoldingMargin=hideFoldingMargin
 
699
            
 
700
            sel_color="#E0E0E0"
 
701
            grouping_color="black"
 
702
            input_color="red"
 
703
            output_color="blue"
 
704
            
 
705
            self.MarkerDefine(GROUPING_SELECTING,    stc.STC_MARK_FULLRECT,
 
706
                              sel_color, sel_color)
 
707
            self.MarkerDefine(IO_SELECTING,          stc.STC_MARK_FULLRECT,
 
708
                              sel_color, sel_color)
 
709
            
 
710
            self.MarkerDefine(GROUPING_START,        stc.STC_MARK_BOXMINUS,
 
711
                              "white", grouping_color)
 
712
            self.MarkerDefine(GROUPING_START_FOLDED, stc.STC_MARK_BOXPLUS,
 
713
                              "white", grouping_color)
 
714
            self.MarkerDefine(GROUPING_MIDDLE,       stc.STC_MARK_VLINE,
 
715
                              "white", grouping_color)
 
716
            self.MarkerDefine(GROUPING_END,          stc.STC_MARK_LCORNER,
 
717
                              "white", grouping_color)
 
718
            
 
719
            self.MarkerDefine(READLINE_BG, stc.STC_MARK_FULLRECT,
 
720
                              wx.Colour(191,191,191), wx.Colour(191,191,191))
 
721
            self.MarkerDefine(INPUT_READLINE, stc.STC_MARK_CHARACTER+ord('<'),
 
722
                              input_color, wx.Colour(191,191,191))
 
723
            
 
724
            if enableShellMode:
 
725
                self.mode='ShellMode'
 
726
            else:
 
727
                self.mode='SlicesMode'
 
728
            
 
729
            self.execOnNextReturn=False
 
730
            if self.mode=='SlicesMode':
 
731
                self.MarkerDefine(INPUT_START,        stc.STC_MARK_BOXMINUS,
 
732
                                  "white", input_color)
 
733
                self.MarkerDefine(INPUT_START_FOLDED, stc.STC_MARK_BOXPLUS,
 
734
                                  "white", input_color)
 
735
                self.MarkerDefine(INPUT_MIDDLE,       stc.STC_MARK_VLINE,
 
736
                                  "white", input_color)
 
737
                self.MarkerDefine(INPUT_END,          stc.STC_MARK_LCORNER,
 
738
                                  "white", input_color)
 
739
            elif self.mode=='ShellMode':
 
740
                self.MarkerDefine(INPUT_START,        stc.STC_MARK_ARROWS,
 
741
                                  input_color, "white")
 
742
                self.MarkerDefine(INPUT_START_FOLDED, stc.STC_MARK_BOXPLUS,
 
743
                                  "white", input_color)
 
744
                self.MarkerDefine(INPUT_MIDDLE,       stc.STC_MARK_DOTDOTDOT,
 
745
                                  input_color, "white")
 
746
                self.MarkerDefine(INPUT_END,          stc.STC_MARK_DOTDOTDOT,
 
747
                                  input_color, "white")
 
748
            
 
749
            self.MarkerDefine(OUTPUT_START,           stc.STC_MARK_BOXMINUS,
 
750
                              "white", output_color)
 
751
            self.MarkerDefine(OUTPUT_START_FOLDED,    stc.STC_MARK_BOXPLUS,
 
752
                              "white", output_color)
 
753
            self.MarkerDefine(OUTPUT_MIDDLE,          stc.STC_MARK_VLINE,
 
754
                              "white", output_color)
 
755
            self.MarkerDefine(OUTPUT_END,             stc.STC_MARK_LCORNER,
 
756
                              "white", output_color)
 
757
            
 
758
            self.MarkerDefine(OUTPUT_BG,             stc.STC_MARK_BACKGROUND,
 
759
                              "white", wx.Colour(242,242,255))
 
760
            
 
761
            # Markers for folding margin...
 
762
            self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN,    stc.STC_MARK_BOXMINUS,
 
763
                              "white", "#808080")
 
764
            self.MarkerDefine(stc.STC_MARKNUM_FOLDER,        stc.STC_MARK_BOXPLUS,
 
765
                              "white", "#808080")
 
766
            self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB,     stc.STC_MARK_VLINE,
 
767
                              "white", "#808080")
 
768
            self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL,    stc.STC_MARK_LCORNER,
 
769
                              "white", "#808080")
 
770
            self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND,     stc.STC_MARK_BOXPLUSCONNECTED,
 
771
                              "white", "#808080")
 
772
            self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED,
 
773
                              "white", "#808080")
 
774
            self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER,
 
775
                              "white", "#808080")
 
776
 
 
777
        # Create the command history.  Commands are added into the
 
778
        # front of the list (ie. at index 0) as they are entered.
 
779
        # self.historyIndex is the current position in the history; it
 
780
        # gets incremented as you retrieve the previous command,
 
781
        # decremented as you retrieve the next, and reset when you hit
 
782
        # Enter.  self.historyIndex == -1 means you're on the current
 
783
        # command, not in the history.
 
784
        self.history = []
 
785
        self.historyIndex = -1
 
786
 
 
787
        #DNM -- disable these markers...
 
788
        #seb add mode for "free edit"
 
789
        self.noteMode = 0
 
790
        #self.MarkerDefine(0,stc.STC_MARK_ROUNDRECT)  # marker for hidden
 
791
        self.searchTxt = ""
 
792
 
 
793
        # Assign handlers for keyboard events.
 
794
        self.Bind(wx.EVT_CHAR, self.OnChar)
 
795
        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
 
796
        
 
797
        self.Bind(wx.stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
 
798
        # TODO : Add a general functions to handle mouse clicks in the
 
799
        # TODO:  STC window whose sole purpose is to make it so
 
800
        # TODO:  that margin selection becomes unselected...
 
801
 
 
802
        # Assign handler for the context menu
 
803
        self.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu)
 
804
        self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI)
 
805
        
 
806
        # Assign handlers for edit events
 
807
        self.Bind(wx.EVT_MENU, lambda evt: self.Cut(), id=wx.ID_CUT)
 
808
        self.Bind(wx.EVT_MENU, lambda evt: self.Copy(), id=wx.ID_COPY)
 
809
        self.Bind(wx.EVT_MENU, lambda evt: self.CopyWithPrompts(), id=frame.ID_COPY_PLUS)
 
810
        self.Bind(wx.EVT_MENU, lambda evt: self.Paste(), id=wx.ID_PASTE)
 
811
        self.Bind(wx.EVT_MENU, lambda evt: self.PasteAndRun(), id=frame.ID_PASTE_PLUS)
 
812
        self.Bind(wx.EVT_MENU, lambda evt: self.SelectAll(), id=wx.ID_SELECTALL)
 
813
        self.Bind(wx.EVT_MENU, lambda evt: self.Clear(), id=wx.ID_CLEAR)
 
814
        self.Bind(wx.EVT_MENU, lambda evt: self.Undo(), id=wx.ID_UNDO)
 
815
        self.Bind(wx.EVT_MENU, lambda evt: self.Redo(), id=wx.ID_REDO)
 
816
        
 
817
        # Assign handler for idle time.
 
818
        self.waiting = False
 
819
        self.Bind(wx.EVT_IDLE, self.OnIdle)
 
820
 
 
821
        # Display the introductory banner information.
 
822
        self.showIntro(introText)
 
823
        
 
824
        outStart,outEnd,inStart,inMiddle,inEnd = [[],[],[],[],[]]
 
825
        
 
826
        # Make "executed startup script move to the top..."
 
827
        if showPySlicesTutorial:
 
828
            self.write(tutorialText,'Output')
 
829
            tutStart=5
 
830
            testStart=16
 
831
            outStart=[tutStart,testStart+3]
 
832
            outEnd=[tutStart-1,testStart-1]
 
833
            inStart=[testStart]
 
834
            inMiddle=[testStart+1]
 
835
            inEnd=[testStart+2]
 
836
        
 
837
        # Assign some pseudo keywords to the interpreter's namespace.
 
838
        self.setBuiltinKeywords()
 
839
 
 
840
        # Add 'shell' to the interpreter's local namespace.
 
841
        self.setLocalShell()
 
842
        
 
843
        # Do this last so the user has complete control over their
 
844
        # environment.  They can override anything they want.
 
845
        if execStartupScript:
 
846
            if startupScript is None:
 
847
                startupScript = os.environ.get('PYTHONSTARTUP')
 
848
            self.execStartupScript(startupScript)
 
849
        else:
 
850
            self.prompt()
 
851
        
 
852
        outStart+=[0]
 
853
        outEnd+=[self.GetLineCount()-2]
 
854
        inStart+=[self.GetLineCount()-1]
 
855
        # Set all the line markers to the proper initial states...
 
856
        for i in range(self.GetLineCount()):
 
857
            self.clearGroupingMarkers(i)
 
858
            self.clearIOMarkers(i)
 
859
            if i in outStart:
 
860
                self.MarkerAdd(i,GROUPING_START)
 
861
                self.MarkerAdd(i,OUTPUT_START)
 
862
                # Background color is confusing for tutorial... skip it!
 
863
                #self.MarkerAdd(i,OUTPUT_BG)
 
864
            elif i in outEnd:
 
865
                self.MarkerAdd(i,GROUPING_END)
 
866
                self.MarkerAdd(i,OUTPUT_END)
 
867
                #self.MarkerAdd(i,OUTPUT_BG)
 
868
            elif i in inStart:
 
869
                self.MarkerAdd(i,GROUPING_START)
 
870
                self.MarkerAdd(i,INPUT_START)
 
871
            elif i in inMiddle:
 
872
                self.MarkerAdd(i,GROUPING_MIDDLE)
 
873
                self.MarkerAdd(i,INPUT_MIDDLE)
 
874
            elif i in inEnd:
 
875
                self.MarkerAdd(i,GROUPING_END)
 
876
                self.MarkerAdd(i,INPUT_END)
 
877
            else:
 
878
                self.MarkerAdd(i,GROUPING_MIDDLE)
 
879
                self.MarkerAdd(i,OUTPUT_MIDDLE)
 
880
                #self.MarkerAdd(i,OUTPUT_BG)
 
881
        
 
882
        self.SliceSelection=False
 
883
        self.runningSlice=None
 
884
        
 
885
        ## NOTE:  See note at bottom of this file...
 
886
        ## #seb: File drag and drop
 
887
        ## self.SetDropTarget( FileDropTarget(self) )
 
888
        
 
889
        #ADD UNDO
 
890
        # Everywhere "ADD UNDO" appears, there is new code to handle markers
 
891
        self.EmptyUndoBuffer()
 
892
        
 
893
        wx.CallAfter(self.ScrollToLine, 0)
 
894
 
 
895
    def ToggleShellMode(self,enableShellMode=None):
 
896
        if enableShellMode==None:
 
897
            if self.mode=='ShellMode':  self.mode='SlicesMode'
 
898
            elif self.mode=='SlicesMode': self.mode='ShellMode'
 
899
        elif enableShellMode:
 
900
            self.mode='ShellMode'
 
901
        else:
 
902
            self.mode='SlicesMode'
 
903
        
 
904
        input_color="red"
 
905
        if self.mode=='SlicesMode':
 
906
            self.MarkerDefine(INPUT_START,           stc.STC_MARK_BOXMINUS,
 
907
                              "white", input_color)
 
908
            self.MarkerDefine(INPUT_START_FOLDED,    stc.STC_MARK_BOXPLUS,
 
909
                              "white", input_color)
 
910
            self.MarkerDefine(INPUT_MIDDLE,          stc.STC_MARK_VLINE,
 
911
                              "white", input_color)
 
912
            self.MarkerDefine(INPUT_END,             stc.STC_MARK_LCORNER,
 
913
                              "white", input_color)
 
914
        elif self.mode=='ShellMode':
 
915
            self.MarkerDefine(INPUT_START,           stc.STC_MARK_ARROWS,
 
916
                              input_color, "white")
 
917
            self.MarkerDefine(INPUT_START_FOLDED,    stc.STC_MARK_BOXPLUS,
 
918
                              "white", input_color)
 
919
            self.MarkerDefine(INPUT_MIDDLE,          stc.STC_MARK_DOTDOTDOT,
 
920
                              input_color, "white")
 
921
            self.MarkerDefine(INPUT_END,             stc.STC_MARK_DOTDOTDOT,
 
922
                              input_color, "white")
 
923
    
 
924
    def ToggleFoldingMargin(self,hideFoldingMargin=None):
 
925
        if hideFoldingMargin==None:
 
926
            self.hideFoldingMargin = not self.hideFoldingMargin
 
927
        else:
 
928
            self.hideFoldingMargin = hideFoldingMargin
 
929
        
 
930
        if self.hideFoldingMargin:
 
931
            self.SetMarginWidth(4, 0)
 
932
        else:
 
933
            self.SetMarginWidth(4, 12)
 
934
 
 
935
    def clearHistory(self):
 
936
        self.history = []
 
937
        self.historyIndex = -1
 
938
        dispatcher.send(signal="SlicesShell.clearHistory")
 
939
 
 
940
 
 
941
    def destroy(self):
 
942
        del self.interp
 
943
 
 
944
    def setFocus(self):
 
945
        """Set focus to the slices shell."""
 
946
        self.SetFocus()
 
947
 
 
948
    def OnIdle(self, event):
 
949
        """Free the CPU to do other things."""
 
950
        if self.waiting:
 
951
            time.sleep(0.05)
 
952
        event.Skip()
 
953
 
 
954
    def showIntro(self, text=''):
 
955
        """Display introductory text in the slices shell."""
 
956
        if text:
 
957
            self.write(text,type='Output')
 
958
        try:
 
959
            if self.interp.introText:
 
960
                if text and not text.endswith(os.linesep):
 
961
                    self.write(os.linesep,type='Output')
 
962
                self.write(self.interp.introText,type='Output')
 
963
        except AttributeError:
 
964
            pass
 
965
    
 
966
    def setBuiltinKeywords(self):
 
967
        """Create pseudo keywords as part of builtins.
 
968
 
 
969
        This sets "close", "exit" and "quit" to a helpful string.
 
970
        """
 
971
        import __builtin__
 
972
        __builtin__.close = __builtin__.exit = __builtin__.quit = \
 
973
            'Click on the close button to leave the application.'
 
974
        __builtin__.cd = cd
 
975
        __builtin__.ls = ls
 
976
        __builtin__.pwd = pwd
 
977
        __builtin__.sx = sx
 
978
 
 
979
 
 
980
    def quit(self):
 
981
        """Quit the application."""
 
982
        # XXX Good enough for now but later we want to send a close event.
 
983
        # In the close event handler we can make sure they want to
 
984
        # quit.  Other applications, like PythonCard, may choose to
 
985
        # hide rather than quit so we should just post the event and
 
986
        # let the surrounding app decide what it wants to do.
 
987
        self.write('Click on the close button to leave the application.',
 
988
                   type='Output')
 
989
 
 
990
 
 
991
    def setLocalShell(self):
 
992
        """Add 'slicesshell' to locals as reference to ShellFacade instance."""
 
993
        self.interp.locals['slicesshell'] = SlicesShellFacade(other=self)
 
994
 
 
995
 
 
996
    def execStartupScript(self, startupScript):
 
997
        """Execute the user's PYTHONSTARTUP script if they have one."""
 
998
        if startupScript and os.path.isfile(startupScript):
 
999
            text = 'Startup script executed: ' + startupScript
 
1000
            self.push('print %r; execfile(%r)' % (text, startupScript))
 
1001
            self.interp.startupScript = startupScript
 
1002
        else:
 
1003
            self.push('')
 
1004
 
 
1005
 
 
1006
    def about(self):
 
1007
        """Display information about Py."""
 
1008
        text = DISPLAY_TEXT % \
 
1009
        (__author__, VERSION, self.revision, self.interp.revision,
 
1010
         sys.version.split()[0], wx.VERSION_STRING, str(wx.PlatformInfo),
 
1011
         sys.platform)
 
1012
        self.write(text.strip(),type='Output')
 
1013
    
 
1014
    def BreakTextIntoCommands(self,text):
 
1015
        """Turn a text block into multiple multi-line commands."""
 
1016
        
 
1017
        #text = text.lstrip() # This should not be done!
 
1018
        text = self.fixLineEndings(text)
 
1019
        text = self.lstripPrompt(text)
 
1020
        text = text.replace(os.linesep, '\n')
 
1021
        lines = text.split('\n')
 
1022
        
 
1023
        continuations = testForContinuations(text)
 
1024
        
 
1025
        if len(continuations)==2: # Error case...
 
1026
            return None,continuations[1]
 
1027
        elif len(continuations)==4:
 
1028
            stringContinuationList,indentationBlockList, \
 
1029
            lineContinuationList,parentheticalContinuationList = continuations
 
1030
        
 
1031
        commands = []
 
1032
        command = ''
 
1033
        for j,line in enumerate(lines):
 
1034
            lstrip = line.lstrip()
 
1035
            
 
1036
            # Get the first alnum word:
 
1037
            first_word=[]
 
1038
            for i in lstrip:
 
1039
                if i.isalnum():
 
1040
                    first_word.append(i)
 
1041
                else:
 
1042
                    break
 
1043
            first_word = ''.join(first_word)
 
1044
            
 
1045
            # Continue the command if it is blank, has indentation,
 
1046
            # starts with else, elif,except, or finally
 
1047
            # or previous line had a line continuation \
 
1048
            
 
1049
            if j==0:
 
1050
                stringCont = False
 
1051
                lineCont=False
 
1052
            else:
 
1053
                stringCont = stringContinuationList[j-1]
 
1054
                lineCont = lineContinuationList[j-1]
 
1055
            
 
1056
            if line.strip() == '' or lstrip != line or \
 
1057
               first_word in ['else','elif','except','finally'] or \
 
1058
               stringCont or lineCont:
 
1059
                # Multiline command. Add to the command.
 
1060
                command += '\n'
 
1061
                command += line
 
1062
            else:
 
1063
                # New command.
 
1064
                if command:
 
1065
                    # Add the previous command to the list.
 
1066
                    commands.append(command)
 
1067
                # Start a new command, which may be multiline.
 
1068
                command = line
 
1069
        
 
1070
        commands.append(command)
 
1071
        
 
1072
        return commands
 
1073
    
 
1074
    def MarkerSet(self,line,markerBitsSet):
 
1075
        """MarkerSet is the Set command for MarkerGet"""
 
1076
        markerBits=self.MarkerGet(line)
 
1077
        
 
1078
        numMarkers=14
 
1079
        for i in range(numMarkers):
 
1080
            if (markerBitsSet & (1<<i)) and not (markerBits & (1<<i)):
 
1081
                self.MarkerAdd(line,i)
 
1082
            elif not (markerBitsSet & (1<<i)) and (markerBits & (1<<i)):
 
1083
                self.MarkerDelete(line,i)
 
1084
    def GetGroupingSlice(self,line_num=None):
 
1085
        """Get the start/stop lines for the slice based on any line in the slice"""
 
1086
        if line_num==None:
 
1087
            line_num=self.GetCurrentLine()
 
1088
        
 
1089
        num_lines=self.GetLineCount()
 
1090
        
 
1091
        for i in range(line_num,-1,-1):
 
1092
            if self.MarkerGet(i) & (1<<GROUPING_START | 1<<GROUPING_START_FOLDED):
 
1093
                break
 
1094
        start_line=i
 
1095
        
 
1096
        addition=0
 
1097
        
 
1098
        for i in range(line_num,num_lines):
 
1099
            if self.MarkerGet(i) & 1<<GROUPING_END:
 
1100
                break
 
1101
            elif (i>line_num) and ( self.MarkerGet(i)
 
1102
                         & (1<<GROUPING_START | 1<<GROUPING_START_FOLDED) ):
 
1103
                addition=-1
 
1104
                break # the solo case...
 
1105
        stop_line=i+addition
 
1106
        
 
1107
        return start_line,stop_line
 
1108
    
 
1109
    def GetIOSlice(self,line_num=None):
 
1110
        """Get the start/stop lines for the slice based on any line in the slice"""
 
1111
        if line_num==None:
 
1112
            line_num=self.GetCurrentLine()
 
1113
        
 
1114
        num_lines=self.GetLineCount()
 
1115
        
 
1116
        for i in range(line_num,-1,-1):
 
1117
            if self.MarkerGet(i) & IO_ANY_START_MASK:
 
1118
                break
 
1119
        start_line=i
 
1120
        
 
1121
        addition=0
 
1122
        
 
1123
        for i in range(line_num,num_lines):
 
1124
            if self.MarkerGet(i) & IO_END_MASK:
 
1125
                break
 
1126
            elif (i>line_num) and (self.MarkerGet(i) & IO_ANY_START_MASK):
 
1127
                addition=-1
 
1128
                break # the solo case...
 
1129
        stop_line=i+addition
 
1130
        
 
1131
        return start_line,stop_line
 
1132
    
 
1133
    def FoldGroupingSlice(self,line_num=None):
 
1134
        if line_num==None:
 
1135
            line_num=self.GetCurrentLine()
 
1136
        
 
1137
        start,end=self.GetGroupingSlice(line_num)
 
1138
        self.HideLines(start+1,end)
 
1139
        marker=self.MarkerGet(start)
 
1140
        self.clearGroupingMarkers(start)
 
1141
        self.MarkerAdd(start,GROUPING_START_FOLDED)
 
1142
        self.clearIOMarkers(start)
 
1143
        if marker & ( 1<<INPUT_START | 1<<INPUT_START_FOLDED ):
 
1144
            self.MarkerAdd(start,INPUT_START_FOLDED)
 
1145
        elif marker & ( 1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED ):
 
1146
            self.MarkerAdd(start,OUTPUT_START_FOLDED)
 
1147
            self.MarkerAdd(start,OUTPUT_BG)
 
1148
        else:
 
1149
            pass #print 'Bad Markers!!!'
 
1150
    def FoldIOSlice(self,line_num=None):
 
1151
        if line_num==None:
 
1152
            line_num=self.GetCurrentLine()
 
1153
        
 
1154
        start,end=self.GetIOSlice(line_num)
 
1155
        self.HideLines(start+1,end)
 
1156
        marker=self.MarkerGet(start)
 
1157
        if (self.MarkerGet(start) & \
 
1158
               (1<<GROUPING_START | 1<<GROUPING_START_FOLDED )) and \
 
1159
               (self.MarkerGet(end) & 1<<GROUPING_END):
 
1160
            self.clearGroupingMarkers(start)
 
1161
            self.MarkerAdd(start,GROUPING_START_FOLDED)
 
1162
        self.clearIOMarkers(start)
 
1163
        if marker & ( 1<<INPUT_START | 1<<INPUT_START_FOLDED ):
 
1164
            self.MarkerAdd(start,INPUT_START_FOLDED)
 
1165
        elif marker & ( 1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED ):
 
1166
            self.MarkerAdd(start,OUTPUT_START_FOLDED)
 
1167
            self.MarkerAdd(start,OUTPUT_BG)
 
1168
        else:
 
1169
            pass #print 'Bad Markers!!!'
 
1170
    def UnFoldGroupingSlice(self,line_num=None):
 
1171
        if line_num==None:
 
1172
            line_num=self.GetCurrentLine()
 
1173
        
 
1174
        start,end=self.GetGroupingSlice(line_num)
 
1175
        self.ShowLines(start+1,end)
 
1176
        self.clearGroupingMarkers(start)
 
1177
        self.MarkerAdd(start,GROUPING_START)
 
1178
        for i in range(start,end):
 
1179
            marker=self.MarkerGet(i)
 
1180
            if marker & (1<<INPUT_START | 1<<INPUT_START_FOLDED):
 
1181
                self.clearIOMarkers(i)
 
1182
                self.MarkerAdd(i,INPUT_START)
 
1183
            elif marker & (1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED):
 
1184
                self.clearIOMarkers(i)
 
1185
                self.MarkerAdd(i,OUTPUT_START)
 
1186
                self.MarkerAdd(i,OUTPUT_BG)
 
1187
        
 
1188
    def UnFoldIOSlice(self,line_num=None):
 
1189
        if line_num==None:
 
1190
            line_num=self.GetCurrentLine()
 
1191
        
 
1192
        start,end=self.GetIOSlice(line_num)
 
1193
        self.ShowLines(start+1,end)
 
1194
        marker=self.MarkerGet(start)
 
1195
        if (self.MarkerGet(start) & \
 
1196
               (1<<GROUPING_START | 1<<GROUPING_START_FOLDED )) and \
 
1197
               (self.MarkerGet(end) & 1<<GROUPING_END):
 
1198
            self.clearGroupingMarkers(start)
 
1199
            self.MarkerAdd(start,GROUPING_START)
 
1200
        self.clearIOMarkers(start)
 
1201
        if marker & 1<<INPUT_START_FOLDED:
 
1202
            self.MarkerAdd(start,INPUT_START)
 
1203
        elif marker & 1<<OUTPUT_START_FOLDED:
 
1204
            self.MarkerAdd(start,OUTPUT_START)
 
1205
            self.MarkerAdd(start,OUTPUT_BG)
 
1206
    
 
1207
    def DeleteOutputSlicesAfter(self,line_num=None):
 
1208
        """Delete all outputs after an input"""
 
1209
        if line_num==None:
 
1210
            line_num=self.GetCurrentLine()
 
1211
        
 
1212
        num_lines=self.GetLineCount()
 
1213
        
 
1214
        if self.MarkerGet(line_num) & OUTPUT_MASK:
 
1215
            #print 'You can only run "DeleteOutputSlicesAfter" from an Input slice!'
 
1216
            return
 
1217
        
 
1218
        startIn,endIn=self.GetIOSlice(line_num)
 
1219
        startGrouping,endGrouping=self.GetGroupingSlice(line_num)
 
1220
        
 
1221
        if endIn<endGrouping:
 
1222
            self.SetSelection(self.PositionFromLine(endIn+1),
 
1223
                              self.PositionFromLine(endGrouping+1))
 
1224
            self.ReplaceSelection('',sliceDeletion=True)
 
1225
        
 
1226
        new_pos=self.GetLineEndPosition(line_num)
 
1227
        self.SetCurrentPos(new_pos)
 
1228
        self.SetSelection(new_pos,new_pos)
 
1229
    
 
1230
    def SplitSlice(self,line_num=None):
 
1231
        if line_num==None:
 
1232
            line_num=self.GetCurrentLine()
 
1233
        
 
1234
        start_num,end_num=self.GetIOSlice(line_num)
 
1235
        
 
1236
        if self.MarkerGet(line_num) & INPUT_MASK:
 
1237
            type='Input'
 
1238
            start=INPUT_START
 
1239
            end=INPUT_END
 
1240
            splitGrouping=True
 
1241
        elif self.MarkerGet(line_num) & OUTPUT_MASK:
 
1242
            type='Output'
 
1243
            start=OUTPUT_START
 
1244
            end=OUTPUT_END
 
1245
            splitGrouping=False
 
1246
        
 
1247
        if start_num==end_num:
 
1248
            return # Can't split one line!
 
1249
        elif start_num==line_num:
 
1250
            self.clearIOMarkers(line_num+1)
 
1251
            self.MarkerAdd(line_num+1,start)
 
1252
            if type=='Output': self.MarkerAdd(line_num+1,OUTPUT_BG)
 
1253
            if splitGrouping:
 
1254
                self.clearGroupingMarkers(line_num+1)
 
1255
                self.MarkerAdd(line_num+1,GROUPING_START)
 
1256
        else:
 
1257
            self.clearIOMarkers(line_num)
 
1258
            self.MarkerAdd(line_num,start)
 
1259
            if type=='Output': self.MarkerAdd(line_num,OUTPUT_BG)
 
1260
            if splitGrouping:
 
1261
                self.clearGroupingMarkers(line_num)
 
1262
                self.MarkerAdd(line_num,GROUPING_START)
 
1263
            if line_num-1>start_num:
 
1264
                self.clearIOMarkers(line_num-1)
 
1265
                self.MarkerAdd(line_num-1,end)
 
1266
                if type=='Output': self.MarkerAdd(line_num-1,OUTPUT_BG)
 
1267
                if splitGrouping:
 
1268
                    self.clearGroupingMarkers(line_num-1)
 
1269
                    self.MarkerAdd(line_num-1,GROUPING_END)
 
1270
    
 
1271
    def BackspaceWMarkers(self,force=False):
 
1272
        # Warning: This is not good at checking for bad markers!
 
1273
        c_before=self.GetCharAt(self.GetCurrentPos() - 1)
 
1274
        c_after=self.GetCharAt(self.GetCurrentPos())
 
1275
        
 
1276
        if c_before==0:
 
1277
            # Disallow deleting the first line or it will destroy the markers...
 
1278
            return False
 
1279
        elif c_before in (ord('\n'),ord('\r')):
 
1280
            line_num=self.GetCurrentLine()
 
1281
            
 
1282
            marker=self.MarkerGet(line_num)
 
1283
            marker_before=self.MarkerGet(line_num-1)
 
1284
            marker_after=self.MarkerGet(line_num+1)
 
1285
            if marker_before & ( 1<<GROUPING_END ) :
 
1286
                return False # Disallow deleting lines between slices...
 
1287
            elif marker & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) :
 
1288
                return False # Disallow deleting lines between slices...
 
1289
            else:
 
1290
                if marker_before & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) :
 
1291
                    self.clearGroupingMarkers(line_num)
 
1292
                elif marker & ( 1<<GROUPING_END ) :
 
1293
                    self.clearGroupingMarkers(line_num-1)
 
1294
            
 
1295
            if (marker_before & 1<<INPUT_END) and force:
 
1296
                # Special case for use in processLine
 
1297
                self.clearIOMarkers(line_num)
 
1298
            elif marker_before & (1<<INPUT_END | 1<<OUTPUT_END):
 
1299
                return False # Disallow deleting lines between slices...
 
1300
            elif marker & ( 1<<INPUT_START | 1<<INPUT_START_FOLDED ) :
 
1301
                return False # Disallow deleting lines between slices...
 
1302
            else:
 
1303
                if marker_before & (1<<INPUT_START  | 1<<INPUT_START_FOLDED |
 
1304
                                    1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED):
 
1305
                    self.clearIOMarkers(line_num)
 
1306
                elif marker & ( 1<<INPUT_END | 1<<OUTPUT_END ) :
 
1307
                    self.clearIOMarkers(line_num-1)
 
1308
        
 
1309
        return True # If everything went well, return True and do the delete...
 
1310
    
 
1311
    def ForwardDeleteWMarkers(self):
 
1312
        c_before=self.GetCharAt(self.GetCurrentPos() - 1)
 
1313
        c_after=self.GetCharAt(self.GetCurrentPos())
 
1314
        if c_after==0:
 
1315
            # Disallow deleting the first line or it will destroy the markers...
 
1316
            return False
 
1317
        elif c_after in (ord('\n'),ord('\r')):
 
1318
            line_num=self.GetCurrentLine()
 
1319
            
 
1320
            marker=self.MarkerGet(line_num)
 
1321
            marker_before=self.MarkerGet(line_num-1)
 
1322
            marker_after=self.MarkerGet(line_num+1)
 
1323
            if marker & ( 1<<GROUPING_END ) :
 
1324
                return False # Disallow deleting lines between slices...
 
1325
            elif marker_after & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) :
 
1326
                return False # Disallow deleting lines between slices...
 
1327
            else:
 
1328
                if marker & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) :
 
1329
                    self.clearGroupingMarkers(line_num+1)
 
1330
                elif marker_after & ( 1<<GROUPING_END ) :
 
1331
                    self.clearGroupingMarkers(line_num)
 
1332
            
 
1333
            if marker & ( 1<<INPUT_END | 1<<OUTPUT_END ) :
 
1334
                return False # Disallow deleting lines between slices...
 
1335
            elif marker_after & ( 1<<INPUT_START | 1<<INPUT_START_FOLDED ) :
 
1336
                return False # Disallow deleting lines between slices...
 
1337
            else:
 
1338
                if marker & (1<<INPUT_START  | 1<<INPUT_START_FOLDED |
 
1339
                             1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED) :
 
1340
                    self.clearIOMarkers(line_num+1)
 
1341
                elif marker_after & ( 1<<INPUT_END | 1<<OUTPUT_END ) :
 
1342
                    self.clearIOMarkers(line_num)
 
1343
        
 
1344
        return True
 
1345
    
 
1346
    def GetIOSelection(self):
 
1347
        started=False
 
1348
        start=0
 
1349
        end=self.GetLineCount()-1
 
1350
        type=None
 
1351
        for i in range(self.GetLineCount()):
 
1352
            if self.MarkerGet(i) & 1<<IO_SELECTING:
 
1353
                if started==False:
 
1354
                    start=i
 
1355
                    if self.MarkerGet(i) & INPUT_MASK:
 
1356
                        type='input'
 
1357
                    elif self.MarkerGet(i) & OUTPUT_MASK:
 
1358
                        type='output'
 
1359
                else:
 
1360
                    if self.MarkerGet(i) & INPUT_MASK:
 
1361
                        if type=='output':
 
1362
                            end=i-1
 
1363
                            break
 
1364
                    elif self.MarkerGet(i) & OUTPUT_MASK:
 
1365
                        if type=='input':
 
1366
                            end=i-1
 
1367
                            break
 
1368
                started=True
 
1369
            elif started==True:
 
1370
                end=i-1
 
1371
                break
 
1372
        
 
1373
        if started==False:
 
1374
            #print 'No Selection!!'
 
1375
            self.SliceSelection=False
 
1376
        
 
1377
        return start,end
 
1378
    
 
1379
    def MergeAdjacentSlices(self):
 
1380
        """This function merges all adjacent selected slices.\n""" + \
 
1381
        """Right now, only IO Merging is allowed."""
 
1382
        started=False
 
1383
        start=0
 
1384
        end=self.GetLineCount()-1
 
1385
        type=None
 
1386
        for i in range(self.GetLineCount()):
 
1387
            if self.MarkerGet(i) & 1<<IO_SELECTING:
 
1388
                if started==False:
 
1389
                    start=i
 
1390
                    if self.MarkerGet(i) & INPUT_MASK:
 
1391
                        type='input'
 
1392
                    elif self.MarkerGet(i) & OUTPUT_MASK:
 
1393
                        type='output'
 
1394
                else:
 
1395
                    if self.MarkerGet(i) & INPUT_MASK:
 
1396
                        if type=='output':
 
1397
                            end=i-1
 
1398
                            break
 
1399
                        else:
 
1400
                            self.clearIOMarkers(i)
 
1401
                            self.clearGroupingMarkers(i)
 
1402
                            self.MarkerAdd(i,INPUT_MIDDLE)
 
1403
                            self.MarkerAdd(i,GROUPING_MIDDLE)
 
1404
                    elif self.MarkerGet(i) & OUTPUT_MASK:
 
1405
                        if type=='input':
 
1406
                            end=i-1
 
1407
                            break
 
1408
                        else:
 
1409
                            self.clearIOMarkers(i)
 
1410
                            self.clearGroupingMarkers(i)
 
1411
                            self.MarkerAdd(i,OUTPUT_MIDDLE)
 
1412
                            self.MarkerAdd(i,OUTPUT_BG)
 
1413
                            self.MarkerAdd(i,GROUPING_MIDDLE)
 
1414
                started=True
 
1415
            elif started==True:
 
1416
                end=i-1
 
1417
                break
 
1418
        
 
1419
        if started and end!=start:
 
1420
            self.clearIOMarkers(end)
 
1421
            self.clearGroupingMarkers(end)
 
1422
            if type=='input':
 
1423
                self.MarkerAdd(end,INPUT_END)
 
1424
                if end+1<self.GetLineCount():
 
1425
                    if self.MarkerGet(end+1) & OUTPUT_MASK:
 
1426
                        self.MarkerAdd(end,GROUPING_MIDDLE)
 
1427
                    else:
 
1428
                        self.MarkerAdd(end,GROUPING_END)
 
1429
                else:
 
1430
                    self.MarkerAdd(end,GROUPING_END)
 
1431
            else:
 
1432
                if self.MarkerGet(start) & 1<<GROUPING_END:
 
1433
                    self.clearGroupingMarkers(start)
 
1434
                    self.MarkerAdd(start,GROUPING_MIDDLE)
 
1435
                self.MarkerAdd(end,OUTPUT_END)
 
1436
                self.MarkerAdd(end,OUTPUT_BG)
 
1437
                self.MarkerAdd(end,GROUPING_END)
 
1438
            
 
1439
    
 
1440
    def SliceSelectionDelete(self):
 
1441
        """Deletion of any selected and possibly discontinuous slices."""
 
1442
        if not self.SliceSelection:
 
1443
            return
 
1444
        
 
1445
        # collect the line numbers to be deleted...
 
1446
        selectedSlices=[]
 
1447
        start,end=None,None
 
1448
        for i in range(self.GetLineCount()):
 
1449
            if self.MarkerGet(i) & (1<<GROUPING_SELECTING | 1<<IO_SELECTING):
 
1450
                if start==None:
 
1451
                    start=i
 
1452
                end=i
 
1453
            elif start!=None:
 
1454
                selectedSlices.append([start,end])
 
1455
                start,end=None,None
 
1456
        if start!=None:
 
1457
            selectedSlices.append([start,end])
 
1458
        
 
1459
        # Unselect everything
 
1460
        self.MarginUnselectAll()
 
1461
        self.SliceSelection=False
 
1462
        
 
1463
        # Going in reverse, delete the selections, fixing the markers as we go...
 
1464
        for i in range(len(selectedSlices)-1,-1,-1):
 
1465
            self.SetSelection(self.PositionFromLine(selectedSlices[i][0]),
 
1466
                              self.GetLineEndPosition(selectedSlices[i][1])+1)
 
1467
            
 
1468
            markerNext = self.MarkerGet(selectedSlices[i][1]+1)
 
1469
            
 
1470
            self.ReplaceSelection('',sliceDeletion=True)
 
1471
            
 
1472
            cur_line=self.GetCurrentLine()
 
1473
            
 
1474
            # If we've made a mess of the grouping markers, clean it up...
 
1475
            if ((self.MarkerGet(cur_line-1) & 1<<GROUPING_END) and
 
1476
               (self.MarkerGet(cur_line) & ( 1<<GROUPING_MIDDLE | 1<<GROUPING_END ) )):
 
1477
                self.clearGroupingMarkers(cur_line)
 
1478
                self.MarkerAdd(cur_line,GROUPING_START)
 
1479
            elif (( self.MarkerGet(cur_line-1) & 1<<GROUPING_MIDDLE ) and
 
1480
                 ( self.MarkerGet(cur_line) &
 
1481
                      ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) )):
 
1482
                self.clearGroupingMarkers(cur_line-1)
 
1483
                self.MarkerAdd(cur_line-1,GROUPING_END)
 
1484
            
 
1485
            if markerNext & 1<<OUTPUT_START:
 
1486
                self.clearIOMarkers(cur_line)
 
1487
                self.MarkerAdd(cur_line,OUTPUT_START)
 
1488
                self.MarkerAdd(cur_line,OUTPUT_BG)
 
1489
            elif markerNext & 1<<OUTPUT_START_FOLDED:
 
1490
                self.clearIOMarkers(cur_line)
 
1491
                self.MarkerAdd(cur_line,OUTPUT_START_FOLDED)
 
1492
                self.MarkerAdd(cur_line,OUTPUT_BG)
 
1493
        
 
1494
        return
 
1495
    
 
1496
    def OnChar(self, event):
 
1497
        """Keypress event handler.
 
1498
 
 
1499
        Only receives an event if OnKeyDown calls event.Skip() for the
 
1500
        corresponding event."""
 
1501
        
 
1502
        if self.noteMode:
 
1503
            event.Skip()
 
1504
            return
 
1505
 
 
1506
        # Prevent modification of output slices
 
1507
        if not self.CanEdit():
 
1508
            return
 
1509
        key = event.GetKeyCode()
 
1510
        currpos = self.GetCurrentPos()
 
1511
        stoppos = self.PositionFromLine(self.GetCurrentLine())
 
1512
        
 
1513
        # Return (Enter) needs to be ignored in this handler.
 
1514
        if key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]:
 
1515
            pass
 
1516
        elif key in self.autoCompleteKeys:
 
1517
            # Usually the dot (period) key activates auto completion.
 
1518
            # Get the command between the prompt and the cursor.  Add
 
1519
            # the autocomplete character to the end of the command.
 
1520
            if self.AutoCompActive():
 
1521
                self.AutoCompCancel()
 
1522
            command = self.GetTextRange(stoppos, currpos) + chr(key)
 
1523
            
 
1524
            # write with undo wrapper...
 
1525
            cpos=self.GetCurrentPos()
 
1526
            s=chr(key)
 
1527
            #ADD UNDO
 
1528
            self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s),
 
1529
                                         forceNewAction=False)
 
1530
            self.write(s,type='Input')
 
1531
            self.UpdateUndoHistoryAfter()
 
1532
            
 
1533
            if self.autoComplete:
 
1534
                self.autoCompleteShow(command)
 
1535
        elif key == ord('('):
 
1536
            # The left paren activates a call tip and cancels an
 
1537
            # active auto completion.
 
1538
            if self.AutoCompActive():
 
1539
                self.AutoCompCancel()
 
1540
            # Get the command between the prompt and the cursor.  Add
 
1541
            # the '(' to the end of the command.
 
1542
            self.ReplaceSelection('')
 
1543
            command = self.GetTextRange(stoppos, currpos) + '('
 
1544
            
 
1545
            # write with undo wrapper...
 
1546
            cpos=self.GetCurrentPos()
 
1547
            s='('
 
1548
            self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s),
 
1549
                                         forceNewAction=True)
 
1550
            self.undoHistory[self.undoIndex]['allowsAppend']=True
 
1551
            self.write(s,type='Input')
 
1552
            self.UpdateUndoHistoryAfter()
 
1553
            
 
1554
            self.autoCallTipShow(command,
 
1555
                                 self.GetCurrentPos() == self.GetTextLength())
 
1556
        else:
 
1557
            # Allow the normal event handling to take place.
 
1558
            # Use undo wrapper
 
1559
            cpos=self.GetCurrentPos()
 
1560
            try:
 
1561
                s=chr(key)
 
1562
                self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s))
 
1563
                event.Skip()
 
1564
                self.UpdateUndoHistoryAfter()
 
1565
            except:
 
1566
                event.Skip()
 
1567
    
 
1568
    def AutoCompActiveCallback(self):
 
1569
        numChars=self.GetTextLength()-self.TotalLengthForAutoCompActiveCallback
 
1570
        if numChars==0:
 
1571
            self.undoIndex-=1
 
1572
            del(self.undoHistory[-1])
 
1573
        else:
 
1574
            uH=self.undoHistory
 
1575
            uI=self.undoIndex
 
1576
            cpos=uH[uI]['posStart']
 
1577
            s=''.join([chr(self.GetCharAt(cpos+i)) for i in range(numChars)])
 
1578
            s.replace(os.linesep,'\n')
 
1579
            self.undoHistory[self.undoIndex]['charList'] = s
 
1580
            self.undoHistory[self.undoIndex]['posEnd'] = cpos + numChars
 
1581
            self.undoHistory[self.undoIndex]['numLines'] = len(s.split('\n'))
 
1582
            self.UpdateUndoHistoryAfter()
 
1583
    
 
1584
    def OnKeyDown(self, event):
 
1585
        """Key down event handler."""
 
1586
 
 
1587
        key = event.GetKeyCode()
 
1588
        # If the auto-complete window is up let it do its thing.
 
1589
        if self.AutoCompActive():
 
1590
            if key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]:
 
1591
                cpos=self.GetCurrentPos()
 
1592
                self.UpdateUndoHistoryBefore('insert','dummy',cpos,cpos+5,
 
1593
                                             forceNewAction=True)
 
1594
                self.undoHistory[self.undoIndex]['allowsAppend'] = True
 
1595
                self.TotalLengthForAutoCompActiveCallback=self.GetTextLength()
 
1596
                event.Skip()
 
1597
                wx.CallAfter(self.AutoCompActiveCallback)
 
1598
            if key in [wx.WXK_DELETE,wx.WXK_BACK]:
 
1599
                self.AutoCompCancel()
 
1600
            else:
 
1601
                event.Skip()
 
1602
            return
 
1603
        
 
1604
        #DNM
 
1605
        # Prevent modification of output slices
 
1606
        controlDown = event.ControlDown()
 
1607
        altDown = event.AltDown()
 
1608
        shiftDown = event.ShiftDown()
 
1609
        currpos = self.GetCurrentPos()
 
1610
        endpos = self.GetTextLength()
 
1611
        selecting = self.GetSelectionStart() != self.GetSelectionEnd()
 
1612
        
 
1613
        if key == wx.WXK_F12: #seb
 
1614
            if self.noteMode:
 
1615
                # self.promptPosStart not used anyway - or ? 
 
1616
##                # We don't need to do this any more!
 
1617
##                self.promptPosEnd = self.PositionFromLine(self.GetLineCount()-1 ) +
 
1618
##                                    len(str(sys.ps1))
 
1619
##                self.GotoLine(self.GetLineCount())
 
1620
##                self.GotoPos(self.promptPosEnd)
 
1621
##                self.prompt()  #make sure we have a prompt
 
1622
                self.SetCaretForeground("black")
 
1623
                self.SetCaretWidth(1)    #default
 
1624
                self.SetCaretPeriod(500) #default
 
1625
            else:
 
1626
                self.SetCaretForeground("red")
 
1627
                self.SetCaretWidth(4)
 
1628
                self.SetCaretPeriod(0) #steady
 
1629
 
 
1630
            self.noteMode = not self.noteMode
 
1631
            return
 
1632
        if self.noteMode:
 
1633
            event.Skip()
 
1634
            return
 
1635
        
 
1636
        doLineBreak=False
 
1637
        doSubmitCommand=False
 
1638
        doPass=False
 
1639
        # Return is used to insert a line break.
 
1640
        # In Shell Mode, hit Return or Enter twice to submit a command
 
1641
        if ((not controlDown and not shiftDown and not altDown) and
 
1642
           key in [wx.WXK_RETURN,]):
 
1643
            if self.mode=='SlicesMode':
 
1644
                doLineBreak=True
 
1645
            elif self.mode=='ShellMode':
 
1646
                startLine,endLine = self.GetIOSlice()
 
1647
                startpos = self.PositionFromLine(startLine)
 
1648
                endpos = self.GetLineEndPosition(endLine)
 
1649
                command = self.GetTextRange(startpos, endpos)
 
1650
                strCont,indentBlock,lineCont,parenCont = testForContinuations(command,ignoreErrors=True)
 
1651
                
 
1652
                lastLine = command.split('\n')[-1]
 
1653
                if lastLine.lstrip()=='': # all whitespace...
 
1654
                    stillIndented=False
 
1655
                elif lastLine[0]==' ':
 
1656
                    stillIndented=True
 
1657
                else:
 
1658
                    stillIndented=False
 
1659
                
 
1660
                if strCont[-1] or indentBlock[-1] or lineCont[-1] or \
 
1661
                   parenCont[-1]:
 
1662
                    doLineBreak=True
 
1663
                elif stillIndented:
 
1664
                    new_pos=self.GetLineEndPosition(endLine)
 
1665
                    self.SetCurrentPos(new_pos)
 
1666
                    self.SetSelection(new_pos,new_pos)
 
1667
                    doLineBreak=True
 
1668
                elif self.GetCurrentLine()!=endLine:
 
1669
                    new_pos=self.GetLineEndPosition(endLine)
 
1670
                    self.SetCurrentPos(new_pos)
 
1671
                    self.SetSelection(new_pos,new_pos)
 
1672
                    doPass = True
 
1673
                else:
 
1674
                    doSubmitCommand=True
 
1675
        # Enter (Shift/Ctrl + Enter/Return) submits a command to the interpreter.
 
1676
        # In Shell Mode, hit Return or Enter twice to submit a command
 
1677
        elif ( key in [wx.WXK_NUMPAD_ENTER,] or
 
1678
            ( (shiftDown or controlDown) and key in [wx.WXK_RETURN,
 
1679
                                                     wx.WXK_NUMPAD_ENTER] ) ):
 
1680
            if self.mode=='SlicesMode':
 
1681
                doSubmitCommand=True
 
1682
            elif self.mode=='ShellMode':
 
1683
                doLineBreak=True
 
1684
        
 
1685
        #Only relevant in ShellMode...
 
1686
        
 
1687
        if doPass:
 
1688
            pass
 
1689
        elif doLineBreak or doSubmitCommand:
 
1690
            if self.CallTipActive():
 
1691
                self.CallTipCancel()
 
1692
            elif self.SliceSelection:
 
1693
                for i in range(self.GetLineCount()):
 
1694
                    if self.MarkerGet(i) & 1<<GROUPING_SELECTING:
 
1695
                        self.DoMarginClick(i, 2, shiftDown, controlDown)
 
1696
                        break
 
1697
                    elif self.MarkerGet(i) & 1<<IO_SELECTING:
 
1698
                        self.DoMarginClick(i, 3, shiftDown, controlDown)
 
1699
                        break
 
1700
            elif doLineBreak:
 
1701
                self.insertLineBreak()
 
1702
                #Only relevant in ShellMode...
 
1703
            elif doSubmitCommand:
 
1704
                self.DeleteOutputSlicesAfter()
 
1705
                self.processLine()
 
1706
        
 
1707
        # Let Ctrl-Alt-* get handled normally.
 
1708
        elif controlDown and altDown:
 
1709
            event.Skip()
 
1710
        
 
1711
        # Clear the current, unexecuted command.
 
1712
        elif key == wx.WXK_ESCAPE:
 
1713
            if self.CallTipActive():
 
1714
                event.Skip()
 
1715
        # Clear the current command
 
1716
        elif key == wx.WXK_BACK and controlDown and shiftDown:
 
1717
            self.clearCommand()
 
1718
 
 
1719
        # Increase font size.
 
1720
        elif controlDown and key in (ord(']'), wx.WXK_NUMPAD_ADD):
 
1721
            dispatcher.send(signal='FontIncrease')
 
1722
 
 
1723
        # Decrease font size.
 
1724
        elif controlDown and key in (ord('['), wx.WXK_NUMPAD_SUBTRACT):
 
1725
            dispatcher.send(signal='FontDecrease')
 
1726
 
 
1727
        # Default font size.
 
1728
        elif controlDown and key in (ord('='), wx.WXK_NUMPAD_DIVIDE):
 
1729
            dispatcher.send(signal='FontDefault')
 
1730
 
 
1731
        # Cut to the clipboard.
 
1732
        elif (controlDown and key in (ord('X'), ord('x'))) \
 
1733
                 or (shiftDown and key == wx.WXK_DELETE):
 
1734
            self.Cut()
 
1735
 
 
1736
        # Copy to the clipboard.
 
1737
        elif controlDown and not shiftDown \
 
1738
                 and key in (ord('C'), ord('c'), wx.WXK_INSERT):
 
1739
            self.Copy()
 
1740
 
 
1741
        # Copy to the clipboard, including prompts.
 
1742
        elif controlDown and shiftDown \
 
1743
                 and key in (ord('C'), ord('c'), wx.WXK_INSERT):
 
1744
            self.CopyWithPrompts()
 
1745
 
 
1746
        # Copy to the clipboard, including prefixed prompts.
 
1747
        elif altDown and not controlDown \
 
1748
                 and key in (ord('C'), ord('c'), wx.WXK_INSERT):
 
1749
            self.CopyWithPromptsPrefixed()
 
1750
        
 
1751
        # Home needs to be aware of the prompt.
 
1752
        elif controlDown and key == wx.WXK_HOME:
 
1753
            # Go to the beginning of the IO Slice
 
1754
            curLine = self.GetCurrentLine()
 
1755
            IOstart = self.GetIOSlice(curLine)[0]
 
1756
            home = self.PositionFromLine(IOstart)
 
1757
            if currpos == home and \
 
1758
               IOstart > 0:
 
1759
                home = self.PositionFromLine(self.GetIOSlice(curLine-1)[0])
 
1760
            self.SetCurrentPos(home)
 
1761
            if not selecting and not shiftDown:
 
1762
                self.SetAnchor(home)
 
1763
                self.EnsureCaretVisible()
 
1764
 
 
1765
        elif controlDown and key == wx.WXK_END:
 
1766
            curLine = self.GetCurrentLine()
 
1767
            IOend = self.GetIOSlice(curLine)[1]
 
1768
            end = self.GetLineEndPosition(IOend)
 
1769
            if currpos == end and \
 
1770
               IOend < self.GetLineCount()-1:
 
1771
                end = self.GetLineEndPosition(self.GetIOSlice(curLine+1)[1])
 
1772
            self.SetCurrentPos(end)
 
1773
            if not selecting and not shiftDown:
 
1774
                self.SetAnchor(end)
 
1775
                self.EnsureCaretVisible()
 
1776
        
 
1777
        elif controlDown and key == wx.WXK_PAGEUP:
 
1778
            pos=0
 
1779
            if currpos > pos:
 
1780
                self.SetCurrentPos(pos)
 
1781
                if not selecting and not shiftDown:
 
1782
                    self.SetAnchor(pos)
 
1783
                    self.EnsureCaretVisible()
 
1784
        
 
1785
        elif controlDown and key == wx.WXK_PAGEDOWN:
 
1786
            pos = self.GetLineEndPosition(self.GetLineCount()-1)
 
1787
            if currpos < pos:
 
1788
                self.SetCurrentPos(pos)
 
1789
                if not selecting and not shiftDown:
 
1790
                    self.SetAnchor(pos)
 
1791
                    self.EnsureCaretVisible()
 
1792
        
 
1793
        elif selecting and key not in NAVKEYS and not self.CanEdit():
 
1794
            pass
 
1795
 
 
1796
        # Paste from the clipboard.
 
1797
        elif (controlDown and not shiftDown and key in (ord('V'), ord('v'))) \
 
1798
                 or (shiftDown and not controlDown and key == wx.WXK_INSERT):
 
1799
            self.Paste()
 
1800
 
 
1801
        # Paste from the clipboard, run commands.
 
1802
        elif controlDown and shiftDown and \
 
1803
                             key in (ord('V'), ord('v')) and self.CanEdit():
 
1804
            self.PasteAndRun()
 
1805
            
 
1806
        # Replace with the previous command from the history buffer.
 
1807
        elif (controlDown and not shiftDown and key == wx.WXK_UP) \
 
1808
              or (altDown and key in (ord('P'), ord('p'))) and self.CanEdit():
 
1809
            self.OnHistoryReplace(step=+1)
 
1810
            
 
1811
        # Replace with the next command from the history buffer.
 
1812
        elif (controlDown and not shiftDown and key == wx.WXK_DOWN) \
 
1813
              or (altDown and key in (ord('N'), ord('n'))) and self.CanEdit():
 
1814
            self.OnHistoryReplace(step=-1)
 
1815
            
 
1816
        # Insert the previous command from the history buffer.
 
1817
        elif (controlDown and shiftDown and key == wx.WXK_UP) and \
 
1818
              self.CanEdit():
 
1819
            self.OnHistoryInsert(step=+1)
 
1820
            
 
1821
        # Insert the next command from the history buffer.
 
1822
        elif (controlDown and shiftDown and key == wx.WXK_DOWN) and \
 
1823
              self.CanEdit():
 
1824
            self.OnHistoryInsert(step=-1)
 
1825
            
 
1826
        # Ctrl-Space shows Auto Completion
 
1827
        # Ctrl-Shift-Space shows CallTips
 
1828
        elif controlDown and key == wx.WXK_SPACE:
 
1829
            self.OnCallTipAutoCompleteManually(shiftDown)
 
1830
        
 
1831
        # Ctrl+Shift+H is used to complete Text (from already typed words)
 
1832
        elif controlDown and shiftDown and key in [ord('H')]:
 
1833
            self.OnShowCompHistory()
 
1834
        
 
1835
        # Search up the history for the text in front of the cursor.
 
1836
        elif key == wx.WXK_F8:
 
1837
            self.OnHistorySearch()
 
1838
            
 
1839
        # Don't backspace over the latest non-continuation prompt.
 
1840
        elif key == wx.WXK_BACK:
 
1841
            if self.SliceSelection:
 
1842
                self.SliceSelectionDelete()
 
1843
                wx.CallAfter(self.RestoreFirstMarker)
 
1844
            elif selecting and self.CanEdit():
 
1845
                self.ReplaceSelection('')
 
1846
                #event.Skip()
 
1847
            elif self.CanEdit():
 
1848
                doDelete=True
 
1849
                cur_line=self.GetCurrentLine()
 
1850
                if not cur_line==0 and \
 
1851
                   self.GetCurrentPos()==self.PositionFromLine(cur_line):
 
1852
                    if self.MarkerGet(cur_line-1) & OUTPUT_MASK:
 
1853
                        doDelete=False
 
1854
                
 
1855
                if doDelete:
 
1856
                    cpos=self.GetCurrentPos()
 
1857
                    s=chr(self.GetCharAt(cpos-1))
 
1858
                    self.UpdateUndoHistoryBefore('delete',s,cpos-1,cpos)
 
1859
                    if self.BackspaceWMarkers():
 
1860
                        event.Skip()
 
1861
                
 
1862
                wx.CallAfter(self.RestoreFirstMarker)
 
1863
        
 
1864
        elif key == wx.WXK_DELETE:
 
1865
            if self.SliceSelection:
 
1866
                self.SliceSelectionDelete()
 
1867
                wx.CallAfter(self.RestoreFirstMarker)
 
1868
            elif selecting and self.CanEdit():
 
1869
                self.ReplaceSelection('')
 
1870
                #event.Skip()
 
1871
            elif self.CanEdit():
 
1872
                doDelete=True
 
1873
                cur_line=self.GetCurrentLine()
 
1874
                if not cur_line==self.GetLineCount()-1 and \
 
1875
                   self.GetCurrentPos()==self.GetLineEndPosition(cur_line):
 
1876
                    if self.MarkerGet(cur_line+1) & OUTPUT_MASK:
 
1877
                        doDelete=False
 
1878
                
 
1879
                if doDelete:
 
1880
                    cpos=self.GetCurrentPos()
 
1881
                    s=chr(self.GetCharAt(cpos))
 
1882
                    self.UpdateUndoHistoryBefore('delete',s,cpos,cpos+1)
 
1883
                    if self.ForwardDeleteWMarkers():
 
1884
                        event.Skip()
 
1885
                
 
1886
                wx.CallAfter(self.RestoreFirstMarker)
 
1887
        
 
1888
        # Only allow these keys after the latest prompt.
 
1889
        elif key == wx.WXK_TAB and self.CanEdit():
 
1890
            # use the same mechanism as with autocmplete...
 
1891
            cpos=self.GetCurrentPos()
 
1892
            self.UpdateUndoHistoryBefore('insert','dummy',cpos,cpos+5,
 
1893
                                         forceNewAction=True)
 
1894
            self.undoHistory[self.undoIndex]['allowsAppend'] = True
 
1895
            self.TotalLengthForAutoCompActiveCallback=self.GetTextLength()
 
1896
            event.Skip()
 
1897
            wx.CallAfter(self.AutoCompActiveCallback)
 
1898
        
 
1899
        # Don't toggle between insert mode and overwrite mode.
 
1900
        elif key == wx.WXK_INSERT:
 
1901
            pass
 
1902
        
 
1903
        # Don't allow line deletion.
 
1904
        #elif controlDown and key in (ord('L'), ord('l')):
 
1905
            # TODO : Allow line deletion eventually ??
 
1906
            #event.Skip()
 
1907
        #    pass
 
1908
        
 
1909
        # Don't allow line transposition.
 
1910
        # Toggle Shell Mode / Slices Mode
 
1911
        elif controlDown and key in (ord('T'), ord('t')):
 
1912
            self.ToggleShellMode()
 
1913
        
 
1914
        #Open and Save now work when using CrustSlicesFrames
 
1915
        elif controlDown and key in (ord('L'), ord('l')):
 
1916
            #print 'Load it'
 
1917
            file=wx.FileSelector("Load File As New Slice")
 
1918
            if file!=u'':
 
1919
                fid=open(file,'r')
 
1920
                self.LoadPyFileAsSlice(fid)
 
1921
                fid.close()
 
1922
        
 
1923
        elif controlDown and key in (ord('D'), ord('d')):
 
1924
            #Disallow line duplication in favor of divide slices
 
1925
            if self.MarkerGet(self.GetCurrentLine()) & INPUT_MASK:
 
1926
                #ADD UNDO
 
1927
                cpos=self.GetCurrentPos()
 
1928
                start,end = map(self.PositionFromLine,
 
1929
                              self.GetGroupingSlice(self.LineFromPosition(cpos)))
 
1930
                self.UpdateUndoHistoryBefore('marker','',start,end,
 
1931
                                             forceNewAction=True)
 
1932
                self.SplitSlice()
 
1933
                # Turn off selecting
 
1934
                self.SetSelection(cpos,cpos)
 
1935
                self.ReplaceSelection('')
 
1936
                self.UpdateUndoHistoryAfter()
 
1937
        
 
1938
        elif controlDown and key in (ord('M'), ord('m')):
 
1939
            #ADD UNDO
 
1940
            if self.SliceSelection:
 
1941
                cpos=self.GetCurrentPos()
 
1942
                ioSel=self.GetIOSelection()
 
1943
            if self.SliceSelection:
 
1944
                start,end = map(self.PositionFromLine,ioSel)
 
1945
                self.UpdateUndoHistoryBefore('marker','',start,end,
 
1946
                                             forceNewAction=True)
 
1947
                self.MergeAdjacentSlices()
 
1948
                # Turn off selecting
 
1949
                self.SetSelection(cpos,cpos)
 
1950
                self.ReplaceSelection('')
 
1951
                self.UpdateUndoHistoryAfter()
 
1952
            
 
1953
        
 
1954
        # Change arrow keys to allow margin behaviors...
 
1955
        elif self.SliceSelection and \
 
1956
             key in [wx.WXK_UP,wx.WXK_DOWN,wx.WXK_RIGHT,wx.WXK_LEFT]:
 
1957
        # TODO : This is useful, but not optimal!
 
1958
            if key==wx.WXK_UP:
 
1959
                for i in range(self.GetLineCount()):
 
1960
                    if self.MarkerGet(i) & 1<<GROUPING_SELECTING:
 
1961
                        if i>0:                     #Grouping
 
1962
                            self.DoMarginClick(i-1, 2, shiftDown, controlDown)
 
1963
                        break
 
1964
                    elif self.MarkerGet(i) & 1<<IO_SELECTING:
 
1965
                        if i>0:                     #IO
 
1966
                            self.DoMarginClick(i-1, 3, shiftDown, controlDown)
 
1967
                        break
 
1968
            elif key==wx.WXK_DOWN:
 
1969
                for i in range(self.GetLineCount()-1,-1,-1):
 
1970
                    if self.MarkerGet(i) & 1<<GROUPING_SELECTING:
 
1971
                        if i<self.GetLineCount()-1: #Grouping
 
1972
                            self.DoMarginClick(i+1, 2, shiftDown, controlDown)
 
1973
                        break
 
1974
                    elif self.MarkerGet(i) & 1<<IO_SELECTING:
 
1975
                        if i<self.GetLineCount()-1: #IO
 
1976
                            self.DoMarginClick(i+1, 3, shiftDown, controlDown)
 
1977
                        break
 
1978
            elif key==wx.WXK_RIGHT:
 
1979
                for i in range(self.GetLineCount()):
 
1980
                    if self.MarkerGet(i) & 1<<GROUPING_SELECTING:
 
1981
                        self.DoMarginClick(i, 3, shiftDown, controlDown)
 
1982
                        break
 
1983
                    elif self.MarkerGet(i) & 1<<IO_SELECTING:
 
1984
                        self.MarginUnselectAll()
 
1985
                        # Go to the beginning of the IO Slice
 
1986
                        self.SetCurrentPos(self.PositionFromLine(i))
 
1987
                        if not selecting and not shiftDown:
 
1988
                            self.SetAnchor(self.PositionFromLine(i))
 
1989
                            self.EnsureCaretVisible()
 
1990
                        break
 
1991
            elif key==wx.WXK_LEFT:
 
1992
                for i in range(self.GetLineCount()):
 
1993
                    if self.MarkerGet(i) & 1<<GROUPING_SELECTING:
 
1994
                        break
 
1995
                    elif self.MarkerGet(i) & 1<<IO_SELECTING:
 
1996
                        self.DoMarginClick(i, 2, shiftDown, controlDown)
 
1997
                        break
 
1998
        # Basic navigation keys should work anywhere.
 
1999
        elif key in NAVKEYS:
 
2000
            event.Skip()
 
2001
        # Protect the readonly portion of the slices shell.
 
2002
        elif not self.CanEdit():
 
2003
            pass
 
2004
        else:
 
2005
            # Check to see if we're selecting
 
2006
            if self.GetSelectionEnd()>self.GetSelectionStart():
 
2007
                # Check to see if a normal input took place
 
2008
                if not controlDown and not altDown and key<256:
 
2009
                    self.ReplaceSelection('') # This seems to work...
 
2010
            event.Skip()
 
2011
        
 
2012
        if self.SliceSelection:
 
2013
            if key not in [wx.WXK_UP,wx.WXK_DOWN,wx.WXK_RIGHT,wx.WXK_LEFT,
 
2014
                           wx.WXK_ALT,wx.WXK_COMMAND,wx.WXK_CONTROL,wx.WXK_SHIFT]:
 
2015
                self.MarginUnselectAll()
 
2016
    
 
2017
    
 
2018
    def MarginSelectAll(self):
 
2019
        num_lines=self.GetLineCount()
 
2020
        for i in range(num_lines):
 
2021
            self.MarkerAdd(i,GROUPING_SELECTING)
 
2022
            self.MarkerDelete(i,IO_SELECTING)
 
2023
    
 
2024
    def MarginUnselectAll(self):
 
2025
        num_lines=self.GetLineCount()
 
2026
        for i in range(num_lines):
 
2027
            self.MarkerDelete(i,IO_SELECTING)
 
2028
            self.MarkerDelete(i,GROUPING_SELECTING)
 
2029
        self.SliceSelection=False
 
2030
    
 
2031
    def DoMarginClick(self, lineClicked, margin, shiftDown, controlDown):
 
2032
        num_lines=self.GetLineCount()
 
2033
        
 
2034
        if margin==1:
 
2035
            pass # these events are not sent right now...
 
2036
        if margin==2:
 
2037
            self.SliceSelection=True
 
2038
            start,end=self.GetGroupingSlice(lineClicked)
 
2039
            startPos=self.PositionFromLine(start)
 
2040
            self.SetCurrentPos(startPos)
 
2041
            self.SetSelection(startPos,startPos)
 
2042
            start_marker=self.MarkerGet(start)
 
2043
            if self.MarkerGet(lineClicked) & 1<<GROUPING_SELECTING:
 
2044
                toggle=self.MarkerDelete
 
2045
                if not shiftDown:
 
2046
                    if start_marker & 1<<GROUPING_START:
 
2047
                        self.FoldGroupingSlice(lineClicked)
 
2048
                    elif start_marker & 1<<GROUPING_START_FOLDED:
 
2049
                        self.UnFoldGroupingSlice(lineClicked)
 
2050
            else:
 
2051
                toggle=self.MarkerAdd
 
2052
            
 
2053
            if not shiftDown:
 
2054
                self.MarginUnselectAll()
 
2055
            
 
2056
            for i in range(start,end+1):
 
2057
                toggle(i,GROUPING_SELECTING)
 
2058
        elif margin==3:
 
2059
            self.SliceSelection=True
 
2060
            start,end=self.GetIOSlice(lineClicked)
 
2061
            startPos=self.PositionFromLine(start)
 
2062
            self.SetCurrentPos(startPos)
 
2063
            self.SetSelection(startPos,startPos)
 
2064
            start_marker=self.MarkerGet(start)
 
2065
            if self.MarkerGet(lineClicked) & 1<<IO_SELECTING:
 
2066
                toggle=self.MarkerDelete
 
2067
                if not shiftDown:
 
2068
                    if start_marker & IO_START_MASK:
 
2069
                        self.FoldIOSlice(lineClicked)
 
2070
                    elif start_marker & IO_START_FOLDED_MASK:
 
2071
                        self.UnFoldIOSlice(lineClicked)
 
2072
            else:
 
2073
                toggle=self.MarkerAdd
 
2074
            
 
2075
            if not shiftDown:
 
2076
                self.MarginUnselectAll()
 
2077
            
 
2078
            for i in range(start,end+1):
 
2079
                toggle(i,IO_SELECTING)
 
2080
            
 
2081
            #print start,end
 
2082
            
 
2083
        elif margin==4:
 
2084
            # TODO : Folding ??
 
2085
            if 1:#self.MarkerGet(lineClicked) & ( 1<<7 | 1<<8 ):
 
2086
                if shiftDown:
 
2087
                    self.SetFoldExpanded(lineClicked, True)
 
2088
                    self.Expand(lineClicked, True, True, 1)
 
2089
                elif controlDown:
 
2090
                    if self.GetFoldExpanded(lineClicked):
 
2091
                        self.SetFoldExpanded(lineClicked, False)
 
2092
                        self.Expand(lineClicked, False, True, 0)
 
2093
                    else:
 
2094
                        self.SetFoldExpanded(lineClicked, True)
 
2095
                        self.Expand(lineClicked, True, True, 100)
 
2096
                else:
 
2097
                    self.ToggleFold(lineClicked)
 
2098
        else:
 
2099
            self.MarginUnselectAll()
 
2100
        if margin in [2,3]:
 
2101
            if toggle==self.MarkerDelete and not shiftDown:
 
2102
                self.SliceSelection=False
 
2103
            else:
 
2104
                self.SliceSelection=True
 
2105
    
 
2106
    def OnMarginClick(self, evt):
 
2107
        
 
2108
        # fold and unfold as neededNAVKEYS
 
2109
        lineClicked = self.LineFromPosition(evt.GetPosition())
 
2110
        self.DoMarginClick(lineClicked,evt.GetMargin(),evt.GetShift(),evt.GetControl())
 
2111
        evt.Skip()
 
2112
 
 
2113
    def OnShowCompHistory(self):
 
2114
        """Show possible autocompletion Words from already typed words."""
 
2115
        
 
2116
        #copy from history
 
2117
        his = self.history[:]
 
2118
        
 
2119
        #put together in one string
 
2120
        joined = " ".join (his)
 
2121
        import re
 
2122
 
 
2123
        #sort out only "good" words
 
2124
        newlist = re.split("[ \.\[\]=}(\)\,0-9\"]", joined)
 
2125
        
 
2126
        #length > 1 (mix out "trash")
 
2127
        thlist = []
 
2128
        for i in newlist:
 
2129
            if len (i) > 1:
 
2130
                thlist.append (i)
 
2131
        
 
2132
        #unique (no duplicate words
 
2133
        #oneliner from german python forum => unique list
 
2134
        unlist = [thlist[i] for i in xrange(len(thlist)) if thlist[i] not in thlist[:i]]
 
2135
            
 
2136
        #sort lowercase
 
2137
        unlist.sort(lambda a, b: cmp(a.lower(), b.lower()))
 
2138
        
 
2139
        #this is more convenient, isn't it?
 
2140
        self.AutoCompSetIgnoreCase(True)
 
2141
        
 
2142
        #join again together in a string
 
2143
        stringlist = " ".join(unlist)
 
2144
 
 
2145
        #pos von 0 noch ausrechnen
 
2146
 
 
2147
        #how big is the offset?
 
2148
        cpos = self.GetCurrentPos() - 1
 
2149
        while chr (self.GetCharAt (cpos)).isalnum():
 
2150
            cpos -= 1
 
2151
            
 
2152
        #the most important part
 
2153
        self.AutoCompShow(self.GetCurrentPos() - cpos -1, stringlist)
 
2154
    
 
2155
    def ReplaceSelection(self,text,sliceDeletion=False,*args,**kwds):
 
2156
        startIO,endIO=self.GetIOSlice()
 
2157
        startGrouping,endGrouping=self.GetGroupingSlice()
 
2158
        startSel = self.LineFromPosition(self.GetSelectionStart())
 
2159
        endSel = self.LineFromPosition(self.GetSelectionEnd())
 
2160
        
 
2161
        #ADD UNDO
 
2162
        cpos=self.GetSelectionStart()
 
2163
        s=self.GetSelectedText()
 
2164
        if s!='':
 
2165
            self.UpdateUndoHistoryBefore('delete',s,cpos,cpos+len(s),
 
2166
                                         forceNewAction=True)
 
2167
        editwindow.EditWindow.ReplaceSelection(self,'',*args,**kwds)
 
2168
        if s!='' and not sliceDeletion:
 
2169
            self.UpdateUndoHistoryAfter()
 
2170
        
 
2171
        if endSel-startSel>0 and not sliceDeletion:
 
2172
            if endSel==endIO and startIO!=self.GetCurrentLine():
 
2173
                self.clearIOMarkers()
 
2174
                self.MarkerAdd(self.GetCurrentLine(),INPUT_END)
 
2175
            
 
2176
            if endSel==endGrouping and startGrouping!=self.GetCurrentLine():
 
2177
                self.clearGroupingMarkers()
 
2178
                self.MarkerAdd(self.GetCurrentLine(),GROUPING_END)
 
2179
        
 
2180
        cpos=self.GetSelectionStart()
 
2181
        s=text
 
2182
        if s!='':
 
2183
            self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s),
 
2184
                                         forceNewAction=True)
 
2185
            self.write(text)
 
2186
            self.UpdateUndoHistoryAfter()
 
2187
        
 
2188
        self.ensureSingleGroupingMarker()
 
2189
        self.ensureSingleIOMarker()
 
2190
        
 
2191
    
 
2192
    def clearCommand(self):
 
2193
        """Delete the current, unexecuted command."""
 
2194
        if not self.CanEdit():
 
2195
            return
 
2196
        start,end=self.GetIOSlice()
 
2197
        startpos = self.PositionFromLine(start)
 
2198
        endpos = self.GetLineEndPosition(end)
 
2199
        self.SetSelection(startpos, endpos)
 
2200
        self.ReplaceSelection('')
 
2201
        self.more = False
 
2202
 
 
2203
    def OnHistoryReplace(self, step):
 
2204
        """Replace with the previous/next command from the history buffer."""
 
2205
        if not self.CanEdit():
 
2206
            return    
 
2207
        self.clearCommand()
 
2208
        self.replaceFromHistory(step)
 
2209
 
 
2210
    def replaceFromHistory(self, step):
 
2211
        """Replace selection with command from the history buffer."""
 
2212
        if not self.CanEdit():
 
2213
            return
 
2214
        self.ReplaceSelection('')
 
2215
        newindex = self.historyIndex + step
 
2216
        if -1 <= newindex <= len(self.history):
 
2217
            self.historyIndex = newindex
 
2218
        if 0 <= newindex <= len(self.history)-1:
 
2219
            command = self.history[self.historyIndex]
 
2220
            command = command.replace('\n', os.linesep)# + ps2)
 
2221
            self.ReplaceSelection(command)
 
2222
 
 
2223
    def OnHistoryInsert(self, step):
 
2224
        """Insert the previous/next command from the history buffer."""
 
2225
        if not self.CanEdit():
 
2226
            return
 
2227
        startpos = self.GetCurrentPos()
 
2228
        self.replaceFromHistory(step)
 
2229
        endpos = self.GetCurrentPos()
 
2230
        self.SetSelection(endpos, startpos)
 
2231
    
 
2232
    # TODO: Fix Me!
 
2233
    def OnHistorySearch(self):
 
2234
        """Search up the history buffer for the text in front of the cursor."""
 
2235
        if not self.CanEdit():
 
2236
            return
 
2237
        startpos = self.GetCurrentPos()
 
2238
        # The text up to the cursor is what we search for.
 
2239
        numCharsAfterCursor = self.GetTextLength() - startpos
 
2240
        searchText = self.getCommand(rstrip=False)
 
2241
        #print 'history search', startpos,numCharsAfterCursor,searchText
 
2242
        if numCharsAfterCursor > 0:
 
2243
            searchText = searchText[:-numCharsAfterCursor]
 
2244
        if not searchText:
 
2245
            return
 
2246
        # Search upwards from the current history position and loop
 
2247
        # back to the beginning if we don't find anything.
 
2248
        if (self.historyIndex <= -1) \
 
2249
        or (self.historyIndex >= len(self.history)-2):
 
2250
            searchOrder = range(len(self.history))
 
2251
        else:
 
2252
            searchOrder = range(self.historyIndex+1, len(self.history)) + \
 
2253
                          range(self.historyIndex)
 
2254
        for i in searchOrder:
 
2255
            command = self.history[i]
 
2256
            if command[:len(searchText)] == searchText:
 
2257
                # Replace the current selection with the one we found.
 
2258
                self.ReplaceSelection(command[len(searchText):])
 
2259
                endpos = self.GetCurrentPos()
 
2260
                self.SetSelection(endpos, startpos)
 
2261
                # We've now warped into middle of the history.
 
2262
                self.historyIndex = i
 
2263
                break
 
2264
 
 
2265
    def setStatusText(self, text):
 
2266
        """Display status information."""
 
2267
 
 
2268
        # This method will likely be replaced by the enclosing app to
 
2269
        # do something more interesting, like write to a status bar.
 
2270
        print text
 
2271
 
 
2272
    def insertLineBreak(self):
 
2273
        """Insert a new line break."""
 
2274
        if not self.CanEdit():
 
2275
            return
 
2276
        elif self.reader.isreading:
 
2277
            self.processLine()
 
2278
            return
 
2279
        
 
2280
        
 
2281
        # write with undo wrapper...
 
2282
        cpos=self.GetCurrentPos()
 
2283
        s=os.linesep
 
2284
        self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+1)
 
2285
        self.write(s,type='Input')
 
2286
        self.UpdateUndoHistoryAfter()
 
2287
        
 
2288
        self.more = True
 
2289
        self.prompt()
 
2290
 
 
2291
    def processLine(self):
 
2292
        """Process the line of text at which the user hit Enter or Shift+RETURN."""
 
2293
        # The user hit ENTER (Shift+RETURN) (Shift+ENTER) and we need to
 
2294
        # decide what to do. They could be sitting on any line in the slices shell.
 
2295
        thepos = self.GetCurrentPos()
 
2296
        cur_line = self.GetCurrentLine()
 
2297
        marker=self.MarkerGet(cur_line)
 
2298
        if marker & INPUT_MASK:
 
2299
            pass
 
2300
        elif marker & OUTPUT_MASK:
 
2301
            return
 
2302
        else:
 
2303
            pass #print 'BLANK LINE!!'
 
2304
        
 
2305
        startline,endline=self.GetIOSlice(cur_line)
 
2306
        
 
2307
        if startline==0:
 
2308
            startpos=0
 
2309
        else:
 
2310
            startpos=self.PositionFromLine(startline)
 
2311
        
 
2312
        endpos=self.GetLineEndPosition(endline)
 
2313
        
 
2314
        # If they hit ENTER inside the current command, execute the command.
 
2315
        if self.CanEdit():
 
2316
            self.SetCurrentPos(endpos)
 
2317
            self.interp.more = False
 
2318
            command = self.GetTextRange(startpos, endpos)
 
2319
            lines = command.split(os.linesep)
 
2320
            lines = [line.rstrip() for line in lines]
 
2321
            command = '\n'.join(lines)
 
2322
            if self.reader.isreading:
 
2323
                if not command:
 
2324
                    # Match the behavior of the standard Python shell
 
2325
                    # when the user hits return without entering a value.
 
2326
                    command = '\n'
 
2327
                self.reader.input = command
 
2328
                self.write(os.linesep,'Input')
 
2329
                self.MarkerSet(self.GetCurrentLine(),READLINE_BG)
 
2330
                self.MarkerSet(self.GetCurrentLine(),INPUT_READLINE)
 
2331
            else:
 
2332
                self.runningSlice = (startline,endline)
 
2333
                self.push(command,useMultiCommand=True)
 
2334
                #print 'command: ',command
 
2335
                wx.FutureCall(1, self.EnsureCaretVisible)
 
2336
                self.runningSlice=None
 
2337
        
 
2338
        skip=self.BackspaceWMarkers(force=True)
 
2339
        if skip:
 
2340
            self.DeleteBack()
 
2341
        
 
2342
        if self.GetCurrentLine()==self.GetLineCount()-1:
 
2343
            self.write(os.linesep,type='Input')
 
2344
            cpos=self.GetCurrentLine()
 
2345
            if self.MarkerGet(cpos-1) & OUTPUT_MASK:
 
2346
                self.MarkerAdd(cpos-1,OUTPUT_BG)
 
2347
            self.SplitSlice()
 
2348
        else:
 
2349
            cur_line=self.GetCurrentLine()
 
2350
            new_pos=self.GetLineEndPosition(cur_line+1)
 
2351
            self.SetSelection(new_pos,new_pos)
 
2352
            self.SetCurrentPos(new_pos)
 
2353
        
 
2354
        self.EmptyUndoBuffer()
 
2355
        self.NeedsCheckForSave=True
 
2356
        if self.hasSyntaxError:
 
2357
            pos=self.GetLineEndPosition(self.syntaxErrorRealLine)
 
2358
            self.SetCurrentPos(pos)
 
2359
            self.SetSelection(pos,pos)
 
2360
    
 
2361
    # Not Used!!
 
2362
    def getMultilineCommand(self, rstrip=True):
 
2363
        """Extract a multi-line command from the editor.
 
2364
 
 
2365
        The command may not necessarily be valid Python syntax."""
 
2366
        # DNM
 
2367
        # XXX Need to extract real prompts here. Need to keep track of
 
2368
        # the prompt every time a command is issued.
 
2369
        text = self.GetCurLine()[0]
 
2370
        line = self.GetCurrentLine()
 
2371
        # Add Marker testing here...
 
2372
        while text == '' and line > 0: # Need to add markers handling...
 
2373
            line -= 1
 
2374
            self.GotoLine(line)
 
2375
            text = self.GetCurLine()[0]
 
2376
        if text=='':
 
2377
            line = self.GetCurrentLine()
 
2378
            self.GotoLine(line)
 
2379
            startpos = self.GetCurrentPos()
 
2380
            line += 1
 
2381
            self.GotoLine(line)
 
2382
            while self.GetCurLine()[0]=='':
 
2383
                line += 1
 
2384
                self.GotoLine(line)
 
2385
            stoppos = self.GetCurrentPos()
 
2386
            command = self.GetTextRange(startpos, stoppos)
 
2387
            command = command.replace(os.linesep, '\n')
 
2388
            command = command.rstrip()
 
2389
            command = command.replace('\n', os.linesep)
 
2390
        else:
 
2391
            command = ''
 
2392
        if rstrip:
 
2393
            command = command.rstrip()
 
2394
        return command
 
2395
 
 
2396
    def getCommand(self, text=None, rstrip=True):
 
2397
        """Extract a command from text which may include a shell prompt.
 
2398
 
 
2399
        The command may not necessarily be valid Python syntax."""
 
2400
        if not text:
 
2401
            text = self.GetCurLine()[0]
 
2402
        # Strip the prompt off the front leaving just the command.
 
2403
        command = self.lstripPrompt(text)
 
2404
        # Change this -- Nothing has prompts!
 
2405
        #if command == text:
 
2406
        #    command = ''  # Real commands have prompts.
 
2407
        if rstrip:
 
2408
            command = command.rstrip()
 
2409
        return command
 
2410
 
 
2411
    def lstripPrompt(self, text):
 
2412
        """Return text without a leading prompt."""
 
2413
        ps1 = str(sys.ps1)
 
2414
        ps1size = len(ps1)
 
2415
        ps2 = str(sys.ps2)
 
2416
        ps2size = len(ps2)
 
2417
        # Strip the prompt off the front of text.
 
2418
        if text[:ps1size] == ps1:
 
2419
            text = text[ps1size:]
 
2420
        elif text[:ps2size] == ps2:
 
2421
            text = text[ps2size:]
 
2422
        return text
 
2423
 
 
2424
    def push(self, command, silent = False,useMultiCommand=False):
 
2425
        """Send command to the interpreter for execution."""
 
2426
        if not silent:
 
2427
            self.write(os.linesep,type='Output')
 
2428
        # TODO : What other magic might we insert here?
 
2429
        # TODO : Is there a good reason not to include magic?
 
2430
        if USE_MAGIC:
 
2431
            command=magic(command)
 
2432
        
 
2433
        # Allows multi-component commands...
 
2434
        self.hasSyntaxError=False
 
2435
        if useMultiCommand:
 
2436
            result = self.BreakTextIntoCommands(command)
 
2437
            if result[0] == None:
 
2438
                commands=[command]
 
2439
                self.hasSyntaxError=True
 
2440
                syntaxErrorLine=result[1]+1
 
2441
                self.syntaxErrorRealLine = self.GetCurrentLine()+result[1]-len(command.split('\n'))
 
2442
            else:
 
2443
                commands=result
 
2444
        else:
 
2445
            commands=[command]
 
2446
        
 
2447
        busy = wx.BusyCursor()
 
2448
        self.waiting = True
 
2449
        self.lastUpdate=None
 
2450
        
 
2451
        for i in commands:
 
2452
            if self.hasSyntaxError:
 
2453
                lineno=syntaxErrorLine
 
2454
                offset=0 # not sure how to easily recover this information...
 
2455
                self.write('  File "<input>", line '+str(lineno)+'\n    '+i.split('\n')[lineno-1]+'\n'+' '*offset+'    ^\nSyntaxError: invalid syntax\n','Error')
 
2456
            else:
 
2457
                self.more = self.interp.push(i+'\n')
 
2458
            # (the \n stops many things from bouncing at the interpreter)
 
2459
            # I could do the following, but I don't really like it!
 
2460
            #if useMultiCommand:
 
2461
            #    self.SplitSlice()
 
2462
        self.lastUpdate=None
 
2463
        
 
2464
        if not silent:
 
2465
            self.MarkerAdd(self.GetIOSlice()[0],OUTPUT_BG)
 
2466
        
 
2467
        self.waiting = False
 
2468
        del busy
 
2469
        if not self.more: # could loop-add to history, too, but I don't like it!
 
2470
            self.addHistory(command.rstrip())
 
2471
        
 
2472
        if not silent:
 
2473
            self.prompt()
 
2474
 
 
2475
    def addHistory(self, command):
 
2476
        """Add command to the command history."""
 
2477
        # Reset the history position.
 
2478
        self.historyIndex = -1
 
2479
        # Insert this command into the history, unless it's a blank
 
2480
        # line or the same as the last command.
 
2481
        if command!='' and ( len(self.history)==0 or command!=self.history[0] ):
 
2482
            self.history.insert(0, command)
 
2483
            dispatcher.send(signal="SlicesShell.addHistory", command=command)
 
2484
    
 
2485
    def clearGroupingMarkers(self,line_num=None):
 
2486
        if line_num==None:
 
2487
            line_num=self.GetCurrentLine()
 
2488
        self.MarkerDelete(line_num,GROUPING_START)
 
2489
        self.MarkerDelete(line_num,GROUPING_START_FOLDED)
 
2490
        self.MarkerDelete(line_num,GROUPING_MIDDLE)
 
2491
        self.MarkerDelete(line_num,GROUPING_END)
 
2492
    def clearIOMarkers(self,line_num=None):
 
2493
        if line_num==None:
 
2494
            line_num=self.GetCurrentLine()
 
2495
        self.MarkerDelete(line_num,INPUT_START)
 
2496
        self.MarkerDelete(line_num,INPUT_START_FOLDED)
 
2497
        self.MarkerDelete(line_num,INPUT_MIDDLE)
 
2498
        self.MarkerDelete(line_num,INPUT_END)
 
2499
        self.MarkerDelete(line_num,OUTPUT_START)
 
2500
        self.MarkerDelete(line_num,OUTPUT_START_FOLDED)
 
2501
        self.MarkerDelete(line_num,OUTPUT_MIDDLE)
 
2502
        self.MarkerDelete(line_num,OUTPUT_END)
 
2503
        self.MarkerDelete(line_num,OUTPUT_BG)
 
2504
        self.MarkerDelete(line_num,READLINE_BG)
 
2505
        self.MarkerDelete(line_num,INPUT_READLINE)
 
2506
    def ensureSingleGroupingMarker(self,line_num=None):
 
2507
        if line_num==None:
 
2508
            line_num=self.GetCurrentLine()
 
2509
        marker=self.MarkerGet(line_num)
 
2510
        if marker & 1<<GROUPING_START:
 
2511
            self.MarkerDelete(line_num,GROUPING_START_FOLDED)
 
2512
            self.MarkerDelete(line_num,GROUPING_MIDDLE)
 
2513
            self.MarkerDelete(line_num,GROUPING_END)
 
2514
        elif marker & 1<<GROUPING_START_FOLDED:
 
2515
            self.MarkerDelete(line_num,GROUPING_MIDDLE)
 
2516
            self.MarkerDelete(line_num,GROUPING_END)
 
2517
        elif marker & 1<<GROUPING_MIDDLE:
 
2518
            self.MarkerDelete(line_num,GROUPING_END)
 
2519
        elif marker & 1<<GROUPING_END:
 
2520
            pass
 
2521
        else:
 
2522
            #print 'ERROR! NO GROUPING MARKERS!'
 
2523
            return 1 # Blank marker
 
2524
        
 
2525
        return 0
 
2526
    
 
2527
    def ensureSingleIOMarker(self,line_num=None):
 
2528
        if line_num==None:
 
2529
            line_num=self.GetCurrentLine()
 
2530
        marker=self.MarkerGet(line_num)
 
2531
        if marker & INPUT_MASK:
 
2532
            self.MarkerDelete(line_num,OUTPUT_START)
 
2533
            self.MarkerDelete(line_num,OUTPUT_START_FOLDED)
 
2534
            self.MarkerDelete(line_num,OUTPUT_MIDDLE)
 
2535
            self.MarkerDelete(line_num,OUTPUT_END)
 
2536
            self.MarkerDelete(line_num,OUTPUT_BG)
 
2537
            [start,start_folded] = [INPUT_START,INPUT_START_FOLDED]
 
2538
            [middle,end] = [INPUT_MIDDLE,INPUT_END]
 
2539
        elif marker & OUTPUT_MASK:
 
2540
            self.MarkerDelete(line_num,INPUT_START)
 
2541
            self.MarkerDelete(line_num,INPUT_START_FOLDED)
 
2542
            self.MarkerDelete(line_num,INPUT_MIDDLE)
 
2543
            self.MarkerDelete(line_num,INPUT_END)
 
2544
            [start,start_folded] = [OUTPUT_START,OUTPUT_START_FOLDED]
 
2545
            [middle,end] = [OUTPUT_MIDDLE,OUTPUT_END]
 
2546
        else:
 
2547
            #print 'ERROR! NO IO MARKERS!'
 
2548
            return 1 # Blank marker
 
2549
        
 
2550
        if marker & 1<<start:
 
2551
            self.MarkerDelete(line_num,start_folded)
 
2552
            self.MarkerDelete(line_num,middle)
 
2553
            self.MarkerDelete(line_num,end)
 
2554
        elif marker & 1<<start_folded:
 
2555
            self.MarkerDelete(line_num,middle)
 
2556
            self.MarkerDelete(line_num,end)
 
2557
        elif marker & 1<<middle:
 
2558
            self.MarkerDelete(line_num,end)
 
2559
        elif marker & 1<<end:
 
2560
            pass
 
2561
        
 
2562
        return 0
 
2563
        
 
2564
    def RestoreFirstMarker(self):
 
2565
        first_marker=self.MarkerGet(0)
 
2566
        self.clearGroupingMarkers(0)
 
2567
        self.clearIOMarkers(0)
 
2568
        
 
2569
        if first_marker & 1<<GROUPING_START :
 
2570
            self.MarkerAdd(0,GROUPING_START)
 
2571
        elif first_marker & 1<<GROUPING_START_FOLDED :
 
2572
            self.MarkerAdd(0,GROUPING_START_FOLDED)
 
2573
        else:
 
2574
            self.MarkerAdd(0,GROUPING_START)
 
2575
        
 
2576
        if first_marker & 1<<INPUT_START :
 
2577
            self.MarkerAdd(0,INPUT_START)
 
2578
        elif first_marker & 1<<INPUT_START_FOLDED :
 
2579
            self.MarkerAdd(0,INPUT_START_FOLDED)
 
2580
        elif first_marker & 1<<OUTPUT_START :
 
2581
            self.MarkerAdd(0,OUTPUT_START)
 
2582
            #self.MarkerAdd(0,OUTPUT_BG) # More harm than good??
 
2583
        elif first_marker & 1<<OUTPUT_START_FOLDED :
 
2584
            self.MarkerAdd(0,OUTPUT_START_FOLDED)
 
2585
            #self.MarkerAdd(0,OUTPUT_BG) # More harm than good??
 
2586
        else:
 
2587
            self.MarkerAdd(0,INPUT_START)
 
2588
        
 
2589
        if self.doHistUpdate:
 
2590
            self.UpdateUndoHistoryAfter()
 
2591
    
 
2592
    def IsAllowedPair(self,m1,m2):
 
2593
        """This testing function ensures that two adjacent markers are valid"""
 
2594
        i_s = 1<<INPUT_START | 1<<INPUT_START_FOLDED
 
2595
        o_s = 1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED
 
2596
        g_s = 1<<GROUPING_START | 1<<GROUPING_START_FOLDED
 
2597
        i_m,o_m,g_m = 1<<INPUT_MIDDLE, 1<<OUTPUT_MIDDLE, 1<<GROUPING_MIDDLE
 
2598
        i_e,o_e,g_e = 1<<INPUT_END, 1<<OUTPUT_END, 1<<GROUPING_END
 
2599
        
 
2600
        if (m1 & i_s) and (m1 & g_s):     #1
 
2601
            if   (m2 & i_s) and (m2 & g_s): return True  #1
 
2602
            elif (m2 & i_m) and (m2 & g_m): return True  #2
 
2603
            elif (m2 & i_e) and (m2 & g_m): return True  #3
 
2604
            elif (m2 & i_e) and (m2 & g_e): return True  #4
 
2605
            elif (m2 & o_s) and (m2 & g_s): return False #5
 
2606
            elif (m2 & o_s) and (m2 & g_m): return True  #6
 
2607
            elif (m2 & o_s) and (m2 & g_e): return True  #7
 
2608
            elif (m2 & o_m) and (m2 & g_m): return False #8
 
2609
            elif (m2 & o_e) and (m2 & g_e): return False #9
 
2610
            else: return False
 
2611
        elif (m1 & i_m) and (m1 & g_m):   #2
 
2612
            if   (m2 & i_m) and (m2 & g_m): return True  #2
 
2613
            elif (m2 & i_e) and (m2 & g_m): return True  #3
 
2614
            elif (m2 & i_e) and (m2 & g_e): return True  #4
 
2615
            else: return False
 
2616
        elif (m1 & i_e) and (m1 & g_m):   #3
 
2617
            if   (m2 & o_s) and (m2 & g_m): return True  #6
 
2618
            elif (m2 & o_s) and (m2 & g_e): return True  #7
 
2619
            else: return False
 
2620
        elif (m1 & i_e) and (m1 & g_e):   #4
 
2621
            if   (m2 & i_s) and (m2 & g_s): return True  #1
 
2622
            elif (m2 & o_s) and (m2 & g_s): return True  #5
 
2623
            else: return False
 
2624
        elif (m1 & o_s) and (m1 & g_s):   #5
 
2625
            if   (m2 & i_s) and (m2 & g_s): return True  #1
 
2626
            elif (m2 & i_m) and (m2 & g_m): return False #2
 
2627
            elif (m2 & i_e) and (m2 & g_m): return False #3
 
2628
            elif (m2 & i_e) and (m2 & g_e): return False #4
 
2629
            elif (m2 & o_s) and (m2 & g_s): return True  #5
 
2630
            elif (m2 & o_s) and (m2 & g_m): return False #6
 
2631
            elif (m2 & o_s) and (m2 & g_e): return False #7
 
2632
            elif (m2 & o_m) and (m2 & g_m): return True  #8
 
2633
            elif (m2 & o_e) and (m2 & g_e): return True  #9
 
2634
            else: return False
 
2635
        elif (m1 & o_s) and (m1 & g_m):   #6
 
2636
            if   (m2 & o_m) and (m2 & g_m): return True  #8
 
2637
            elif (m2 & o_e) and (m2 & g_e): return True  #9
 
2638
            else: return False
 
2639
        elif (m1 & o_s) and (m1 & g_e):   #7
 
2640
            if   (m2 & i_s) and (m2 & g_s): return True  #1
 
2641
            elif (m2 & o_s) and (m2 & g_s): return True  #5
 
2642
            else: return False
 
2643
        elif (m1 & o_m) and (m1 & g_m):   #8
 
2644
            if   (m2 & o_m) and (m2 & g_m): return True  #8
 
2645
            elif (m2 & o_e) and (m2 & g_e): return True  #9
 
2646
            else: return False
 
2647
        elif (m1 & o_e) and (m1 & g_e):   #9
 
2648
            if   (m2 & i_s) and (m2 & g_s): return True  #1
 
2649
            elif (m2 & o_s) and (m2 & g_s): return True  #5
 
2650
            else: return False
 
2651
        else:
 
2652
            return False
 
2653
            
 
2654
        
 
2655
    def CleanAllMarkers(self):
 
2656
        self.RestoreFirstMarker()
 
2657
        first_marker=self.MarkerGet(0)
 
2658
        last_line_num=self.GetLineCount()-1
 
2659
        
 
2660
        for i in range(1,last_line_num):
 
2661
            self.ensureSingleGroupingMarker(i)
 
2662
            self.ensureSingleIOMarker(i)
 
2663
            
 
2664
            previous_marker=self.MarkerGet(i-1)
 
2665
            marker=self.MarkerGet(i)
 
2666
            
 
2667
            if not self.IsAllowedPair(previous_marker,marker):
 
2668
                pass # FIX MARKER!!
 
2669
            # FIX ME
 
2670
    
 
2671
    def write(self, text,type='Input',silent=False):
 
2672
        """Display text in the slices shell.
 
2673
 
 
2674
        Replace line endings with OS-specific endings."""
 
2675
        text = self.fixLineEndings(text)
 
2676
        split=text.split(os.linesep)
 
2677
        self.AddText(text)
 
2678
        
 
2679
        # This part handles all the marker stuff that accompanies
 
2680
        # adding or removing new lines of text...
 
2681
        # Get the total number of lines in the Document == last line number
 
2682
        last_line_num=self.GetLineCount()-1
 
2683
        # Get the line number we ended on in the write
 
2684
        end_line_num=self.GetCurrentLine()
 
2685
        # Get the number of returns we are using == number of lines we pasted -1
 
2686
        num_new_lines=text.count(os.linesep)
 
2687
        # So if num_new_lines==0, start_line_num and end_line_num are the same
 
2688
        start_line_num=end_line_num-num_new_lines+1
 
2689
        
 
2690
        # This is a little unnecessary because there will always
 
2691
        # be a line before if we just inserted a newline!
 
2692
        if start_line_num == 0:
 
2693
            previous_line_num=None
 
2694
        else:
 
2695
            previous_line_num=start_line_num-1
 
2696
        
 
2697
        #However, this is very important...
 
2698
        if end_line_num == last_line_num:
 
2699
            next_line_num=None
 
2700
        else:
 
2701
            next_line_num=end_line_num+1
 
2702
        
 
2703
        if type=='Input':
 
2704
            start = INPUT_START
 
2705
            start_folded = INPUT_START_FOLDED
 
2706
            middle = INPUT_MIDDLE
 
2707
            end = INPUT_END
 
2708
            # preparation for more io types...
 
2709
            opposite_start_mask = 1<<OUTPUT_START
 
2710
            opposite_start_folded_mask = 1<<OUTPUT_START_FOLDED
 
2711
            opposite_middle_mask = 1<<OUTPUT_MIDDLE # To test for bad writes...
 
2712
            opposite_end_mask = 1<<OUTPUT_END # To test for bad writes...
 
2713
        elif type in ['Output','Error']:
 
2714
            #self.MarkerAdd(start_line_num,GROUPING_START_FOLDED)
 
2715
            start=OUTPUT_START
 
2716
            start_folded=OUTPUT_START_FOLDED
 
2717
            middle=OUTPUT_MIDDLE
 
2718
            end=OUTPUT_END
 
2719
            # preparation for more io types...
 
2720
            opposite_start_mask = 1<<INPUT_START
 
2721
            opposite_start_folded_mask = 1<<INPUT_START_FOLDED
 
2722
            opposite_middle_mask = 1<<INPUT_MIDDLE # To test for bad writes...
 
2723
            opposite_end_mask = 1<<INPUT_END # To test for bad writes...
 
2724
        
 
2725
        if num_new_lines>0: #Do nothing if typing within a line...
 
2726
            # Update the Grouping Markers
 
2727
            # For the previous line and the start_line
 
2728
            # Test to make sure we can write ... but not here ...
 
2729
            #    test this before we call write or before we add text...
 
2730
            # So we assume it already obeys the rules 
 
2731
            
 
2732
            badMarkers=False
 
2733
            fixIOEnd=True
 
2734
            
 
2735
            if previous_line_num==None:
 
2736
                # This is an impossible case, here just for completeness...
 
2737
                self.clearGroupingMarkers(start_line_num)
 
2738
                self.MarkerAdd(start_line_num,GROUPING_START)
 
2739
                
 
2740
                self.clearIOMarkers(start_line_num)
 
2741
                self.MarkerAdd(start_line_num,start)
 
2742
                if type in ['Output','Error']: self.MarkerAdd(start_line_num,OUTPUT_BG)
 
2743
            else:
 
2744
                previous_marker=self.MarkerGet(previous_line_num)
 
2745
                if previous_marker & opposite_middle_mask:
 
2746
                    badMarkers=True
 
2747
            
 
2748
            if next_line_num==None:
 
2749
                self.MarkerAdd(end_line_num,GROUPING_END)
 
2750
                self.MarkerAdd(end_line_num,end)
 
2751
                if type in ['Output','Error']: self.MarkerAdd(end_line_num,OUTPUT_BG)
 
2752
                fixEndMarkers=False
 
2753
                # May be overwritten below if start_line_num==end_line_num...
 
2754
            else:
 
2755
                next_marker=self.MarkerGet(next_line_num)
 
2756
                fixEndMarkers=True
 
2757
                if next_marker & ( opposite_middle_mask | opposite_end_mask ):
 
2758
                    badMarkers=True
 
2759
            
 
2760
            if not badMarkers:
 
2761
                # ensure previous_line only has one marker & turn end into middle
 
2762
                if previous_line_num!=None:
 
2763
                    # Adjust previous line appropriately, ensure only one marker
 
2764
                    # Only print errors if we are on input!
 
2765
                    blank=False
 
2766
                    blank=blank or self.ensureSingleGroupingMarker(previous_line_num)
 
2767
                    blank=blank or self.ensureSingleIOMarker(previous_line_num)
 
2768
                    
 
2769
                    if blank:
 
2770
                        #if type=='Input' and not silent: print 'BLANK LINE!' # BAD CASE
 
2771
                        pass
 
2772
                    
 
2773
                    if previous_marker & 1<<GROUPING_END :
 
2774
                        # Make GROUPING slice continue unless we hit
 
2775
                        #  an output end and are starting a new input...
 
2776
                        if (previous_marker & OUTPUT_MASK) and type=='Input':
 
2777
                            pass
 
2778
                        else:
 
2779
                            self.MarkerDelete(previous_line_num,GROUPING_END)
 
2780
                            # ONLY CHANGING CASE
 
2781
                            self.MarkerAdd(previous_line_num,GROUPING_MIDDLE)
 
2782
                    
 
2783
                    if previous_marker & 1<<end :
 
2784
                        self.MarkerDelete(previous_line_num,end)
 
2785
                        self.MarkerAdd(previous_line_num,middle) # ONLY CHANGING CASE
 
2786
                        if type in ['Output','Error']: self.MarkerAdd(previous_line_num,OUTPUT_BG)
 
2787
                    elif previous_marker & opposite_middle_mask :
 
2788
                         # BAD CASE
 
2789
                        if type=='Input' and not silent:
 
2790
                            #print 'Should have been a bad marker!'
 
2791
                            pass
 
2792
                    
 
2793
                    # We can only add input to an input slice
 
2794
                    # And can only add output to an output slice
 
2795
                    
 
2796
                    if previous_marker & ( opposite_start_mask |
 
2797
                                           opposite_start_folded_mask |
 
2798
                                           opposite_end_mask ):
 
2799
                        if type=='Input':
 
2800
                            self.clearGroupingMarkers(start_line_num)
 
2801
                            self.MarkerAdd(start_line_num,GROUPING_START)
 
2802
                            if start_line_num==end_line_num:
 
2803
                                fixEndMarkers=False
 
2804
                        else:
 
2805
                            if start_line_num==end_line_num:
 
2806
                                fixIOEnd=False
 
2807
                        self.clearIOMarkers(start_line_num)
 
2808
                        self.MarkerAdd(start_line_num,start)
 
2809
                        if type in ['Output','Error']: self.MarkerAdd(start_line_num,OUTPUT_BG)
 
2810
                    else:
 
2811
                        if next_line_num!=None:
 
2812
                            self.clearGroupingMarkers(start_line_num)
 
2813
                            self.clearIOMarkers(start_line_num)
 
2814
                            self.MarkerAdd(start_line_num,GROUPING_MIDDLE)
 
2815
                            self.MarkerAdd(start_line_num,middle)
 
2816
                            if type in ['Output','Error']: self.MarkerAdd(start_line_num,OUTPUT_BG)
 
2817
                            # This may be overwritten if start_line_num==end_line_num
 
2818
                
 
2819
                # Take care of all the middle lines...
 
2820
                # Does nothing for only one line...
 
2821
                for i in range(start_line_num,end_line_num):
 
2822
                    self.clearGroupingMarkers(i)
 
2823
                    self.MarkerAdd(i,GROUPING_MIDDLE)
 
2824
                    
 
2825
                    self.clearIOMarkers(i)
 
2826
                    self.MarkerAdd(i,middle)
 
2827
                    if type in ['Output','Error']: self.MarkerAdd(i,OUTPUT_BG)
 
2828
                
 
2829
                if fixEndMarkers:
 
2830
                    # Take care of the end_line if we haven't already done so...
 
2831
                    blank=False
 
2832
                    blank=blank or self.ensureSingleGroupingMarker(next_line_num)
 
2833
                    blank=blank or self.ensureSingleIOMarker(next_line_num)
 
2834
                    
 
2835
                    if blank:
 
2836
                        if type=='Input' and not silent:
 
2837
                            #print 'BLANK LINE!' # BAD CASE
 
2838
                            pass
 
2839
                    
 
2840
                    self.clearGroupingMarkers(end_line_num)
 
2841
                    if fixIOEnd:
 
2842
                        self.clearIOMarkers(end_line_num)
 
2843
                    
 
2844
                    if next_marker & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) :
 
2845
                        self.MarkerAdd(end_line_num,GROUPING_END)
 
2846
                    elif next_marker & ( 1<<GROUPING_MIDDLE | 1<<GROUPING_END ) :
 
2847
                        self.MarkerAdd(end_line_num,GROUPING_MIDDLE)
 
2848
                    
 
2849
                    if fixIOEnd: 
 
2850
                        if next_marker & ( 1<<start | 1<<start_folded ) :
 
2851
                            self.MarkerAdd(end_line_num,end)
 
2852
                            if type in ['Output','Error']: self.MarkerAdd(end_line_num,OUTPUT_BG)
 
2853
                        elif next_marker & ( 1<<middle | 1<<end ) :
 
2854
                            self.MarkerAdd(end_line_num,middle)
 
2855
                            if type in ['Output','Error']: self.MarkerAdd(end_line_num,OUTPUT_BG)
 
2856
                        elif next_marker & ( opposite_start_mask |
 
2857
                                             opposite_start_folded_mask ):
 
2858
                            self.MarkerAdd(end_line_num,end)
 
2859
                            if type in ['Output','Error']: self.MarkerAdd(end_line_num,OUTPUT_BG)
 
2860
                        else:
 
2861
                            self.MarkerAdd(end_line_num,start_folded)
 
2862
                            if type in ['Output','Error']: self.MarkerAdd(end_line_num,OUTPUT_BG)
 
2863
                            if type=='Input' and not silent:
 
2864
                                #print 'BAD MARKERS!'
 
2865
                                pass
 
2866
            else:
 
2867
                if type=='Input' and not silent:
 
2868
                    #print 'BAD MARKERS!!!'
 
2869
                    pass
 
2870
        
 
2871
        self.EnsureCaretVisible()
 
2872
        
 
2873
        if self.waiting:
 
2874
            if self.lastUpdate==None:
 
2875
                self.lastUpdate=time.time()
 
2876
            if time.time()-self.lastUpdate > PRINT_UPDATE_MAX_TIME:
 
2877
                self.Update()
 
2878
                self.lastUpdate=time.time()
 
2879
    
 
2880
    def fixLineEndings(self, text):
 
2881
        """Return text with line endings replaced by OS-specific endings."""
 
2882
        lines = text.split('\r\n')
 
2883
        for l in range(len(lines)):
 
2884
            chunks = lines[l].split('\r')
 
2885
            for c in range(len(chunks)):
 
2886
                chunks[c] = os.linesep.join(chunks[c].split('\n'))
 
2887
            lines[l] = os.linesep.join(chunks)
 
2888
        text = os.linesep.join(lines)
 
2889
        return text
 
2890
 
 
2891
    def prompt(self): # Autoindent added!!!
 
2892
        """Display proper prompt for the context: ps1, ps2 or ps3.
 
2893
        
 
2894
        If this is a continuation line, autoindent as necessary."""
 
2895
        # TODO : How much of this can I do away with now without prompts??
 
2896
        
 
2897
        isreading = self.reader.isreading
 
2898
        
 
2899
        skip = True
 
2900
        if isreading:
 
2901
            prompt = str(sys.ps3)
 
2902
        elif self.more:
 
2903
            prompt = str(sys.ps2)
 
2904
        else:
 
2905
            prompt = str(sys.ps1)
 
2906
        pos = self.GetCurLine()[1]
 
2907
        if pos > 0:
 
2908
            if isreading:
 
2909
                skip = True
 
2910
            else:
 
2911
                self.write(os.linesep,type='Input')
 
2912
        if not self.more:
 
2913
            # Not needed anymore! # self.promptPosStart = self.GetCurrentPos()
 
2914
            pass
 
2915
        if not skip:
 
2916
            self.write(prompt,type='Input')
 
2917
        if not self.more:
 
2918
            # Not needed anymore! # self.promptPosEnd = self.GetCurrentPos()
 
2919
            # Clear the undo history after running a command.
 
2920
            self.EmptyUndoBuffer()
 
2921
        
 
2922
        #DNM/CP
 
2923
        # Autoindent magic
 
2924
        # Match the indent of the line above
 
2925
        # UNLESS the line above ends in a colon...then add four spaces
 
2926
        # (after valid keywords (if, else, etc...) only)
 
2927
        if self.more:
 
2928
            line_num=self.GetCurrentLine()
 
2929
            currentLine=self.GetLine(line_num)
 
2930
            previousLine=self.GetLine(line_num-1)
 
2931
            pstrip=previousLine.strip()
 
2932
            lstrip=previousLine.lstrip()
 
2933
            
 
2934
            if pstrip == '':
 
2935
                # because it is all whitespace!
 
2936
                indent=previousLine.strip('\n').strip('\r')
 
2937
            else:
 
2938
                indent=previousLine[:(len(previousLine)-len(lstrip))]
 
2939
                if testForContinuations(previousLine,ignoreErrors=True)[1][0]:
 
2940
                    indent+=' '*4
 
2941
            
 
2942
            #ADD UNDO
 
2943
            cpos=self.GetCurrentPos()
 
2944
            s=indent
 
2945
            self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s))
 
2946
            self.write(s,type='Input')
 
2947
            self.UpdateUndoHistoryAfter()
 
2948
            
 
2949
            
 
2950
        self.EnsureCaretVisible()
 
2951
        self.ScrollToColumn(0)
 
2952
 
 
2953
    def readline(self):
 
2954
        """Replacement for stdin.readline()."""
 
2955
        input = ''
 
2956
        reader = self.reader
 
2957
        reader.isreading = True
 
2958
        self.prompt()
 
2959
        
 
2960
        # Ensure that we get a new line and that it's got an input marker...
 
2961
        # Also need to temporarily block any other action...
 
2962
        cLine = self.GetCurrentLine()
 
2963
        self.clearIOMarkers(cLine)
 
2964
        self.MarkerAdd(cLine,INPUT_START)
 
2965
        self.MarkerAdd(cLine,READLINE_BG)
 
2966
        self.MarkerAdd(cLine,INPUT_READLINE)
 
2967
        
 
2968
        try:
 
2969
            while not reader.input:
 
2970
                wx.YieldIfNeeded()
 
2971
            input = reader.input
 
2972
        finally:
 
2973
            start,end = self.GetIOSlice()
 
2974
            start = self.runningSlice[1] + 1
 
2975
            for i in range(start,end+1):
 
2976
                self.clearIOMarkers(i)
 
2977
                self.clearGroupingMarkers(i)
 
2978
                self.MarkerAdd(i,OUTPUT_BG)
 
2979
                if i == start:    self.MarkerAdd(i,OUTPUT_START)
 
2980
                elif i==end:      self.MarkerAdd(i,OUTPUT_END)
 
2981
                else:             self.MarkerAdd(i,OUTPUT_MIDDLE)
 
2982
                
 
2983
                if i==end:        self.MarkerAdd(i,GROUPING_END)
 
2984
                else:             self.MarkerAdd(i,GROUPING_MIDDLE)
 
2985
            reader.input = ''
 
2986
            reader.isreading = False
 
2987
        input = str(input)  # In case of Unicode.
 
2988
        return input
 
2989
 
 
2990
    def readlines(self):
 
2991
        """Replacement for stdin.readlines()."""
 
2992
        lines = []
 
2993
        while lines[-1:] != ['\n']:
 
2994
            lines.append(self.readline())
 
2995
        return lines
 
2996
 
 
2997
    def raw_input(self, prompt=''):
 
2998
        """Return string based on user input."""
 
2999
        if prompt:
 
3000
            self.write(prompt,type='Output')
 
3001
        return self.readline()
 
3002
 
 
3003
    def ask(self, prompt='Please enter your response:'):
 
3004
        """Get response from the user using a dialog box."""
 
3005
        dialog = wx.TextEntryDialog(None, prompt,
 
3006
                                    'Input Dialog (Raw)', '')
 
3007
        try:
 
3008
            if dialog.ShowModal() == wx.ID_OK:
 
3009
                text = dialog.GetValue()
 
3010
                return text
 
3011
        finally:
 
3012
            dialog.Destroy()
 
3013
        return ''
 
3014
 
 
3015
    def pause(self):
 
3016
        """Halt execution pending a response from the user."""
 
3017
        self.ask('Press enter to continue:')
 
3018
 
 
3019
    def clear(self):
 
3020
        """Delete all text from the slices shell."""
 
3021
        self.ClearAll()
 
3022
        self.MarkerAdd(0,GROUPING_START)
 
3023
        self.MarkerAdd(0,INPUT_START)
 
3024
 
 
3025
    def run(self, command, prompt=True, verbose=True):
 
3026
        """Execute command as if it was typed in directly.
 
3027
        >>> shell.run('print "this"')
 
3028
        >>> print "this"
 
3029
        this
 
3030
        >>>
 
3031
        """
 
3032
        # Go to the very bottom of the text.
 
3033
        endpos = self.GetTextLength()
 
3034
        self.SetCurrentPos(endpos)
 
3035
        command = command.rstrip()
 
3036
        if prompt: self.prompt()
 
3037
        if verbose: self.write(command,type='Input')
 
3038
        self.push(command)
 
3039
 
 
3040
    # TODO : Will have to fix this to handle other kinds of errors mentioned before...
 
3041
    def runfile(self, filename):
 
3042
        """Execute all commands in file as if they were typed into the shell."""
 
3043
        file = open(filename)
 
3044
        try:
 
3045
            self.prompt()
 
3046
            for command in file.readlines():
 
3047
                if command[:6] == 'shell.':
 
3048
                    # Run shell methods silently.
 
3049
                    self.run(command, prompt=False, verbose=False)
 
3050
                else:
 
3051
                    self.run(command, prompt=False, verbose=True)
 
3052
        finally:
 
3053
            file.close()
 
3054
 
 
3055
    def autoCompleteShow(self, command, offset = 0):
 
3056
        """Display auto-completion popup list."""
 
3057
        self.AutoCompSetAutoHide(self.autoCompleteAutoHide)
 
3058
        self.AutoCompSetIgnoreCase(self.autoCompleteCaseInsensitive)
 
3059
        list = self.interp.getAutoCompleteList(command,
 
3060
                    includeMagic=self.autoCompleteIncludeMagic,
 
3061
                    includeSingle=self.autoCompleteIncludeSingle,
 
3062
                    includeDouble=self.autoCompleteIncludeDouble)
 
3063
        if list:
 
3064
            options = ' '.join(list)
 
3065
            #offset = 0
 
3066
            self.AutoCompShow(offset, options)
 
3067
 
 
3068
    def autoCallTipShow(self, command, insertcalltip = True, forceCallTip = False):
 
3069
        """Display argument spec and docstring in a popup window."""
 
3070
        if self.CallTipActive():
 
3071
            self.CallTipCancel()
 
3072
        (name, argspec, tip) = self.interp.getCallTip(command)
 
3073
        if tip:
 
3074
            dispatcher.send(signal='SlicesShell.calltip', sender=self, calltip=tip)
 
3075
        if not self.autoCallTip and not forceCallTip:
 
3076
            return
 
3077
        startpos = self.GetCurrentPos()
 
3078
        if argspec and insertcalltip and self.callTipInsert:
 
3079
            # write with undo history...
 
3080
            cpos=self.GetCurrentPos()
 
3081
            s=argspec + ')'
 
3082
            self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s))
 
3083
            self.write(s,type='Input')
 
3084
            self.UpdateUndoHistoryAfter()
 
3085
            
 
3086
            endpos = self.GetCurrentPos()
 
3087
            self.SetSelection(startpos, endpos)
 
3088
        if tip:
 
3089
            tippos = startpos - (len(name) + 1)
 
3090
            fallback = startpos - self.GetColumn(startpos)
 
3091
            # In case there isn't enough room, only go back to the fallback.
 
3092
            tippos = max(tippos, fallback)
 
3093
            self.CallTipShow(tippos, tip)
 
3094
    
 
3095
    def OnCallTipAutoCompleteManually (self, shiftDown):
 
3096
        """AutoComplete and Calltips manually."""
 
3097
        if self.AutoCompActive():
 
3098
            self.AutoCompCancel()
 
3099
        currpos = self.GetCurrentPos()
 
3100
        stoppos = self.PositionFromLine(self.GetIOSlice()[0])
 
3101
 
 
3102
        cpos = currpos
 
3103
        #go back until '.' is found
 
3104
        pointavailpos = -1
 
3105
        while cpos >= stoppos:
 
3106
            if self.GetCharAt(cpos) == ord ('.'):
 
3107
                pointavailpos = cpos
 
3108
                break
 
3109
            cpos -= 1
 
3110
 
 
3111
        #word from non whitespace until '.'
 
3112
        if pointavailpos != -1:
 
3113
            #look backward for first whitespace char
 
3114
            textbehind = self.GetTextRange (pointavailpos + 1, currpos)
 
3115
            pointavailpos += 1
 
3116
 
 
3117
            if not shiftDown:
 
3118
                #call AutoComplete
 
3119
                stoppos = self.PositionFromLine(self.GetIOSlice()[0])
 
3120
                textbefore = self.GetTextRange(stoppos, pointavailpos)
 
3121
                self.autoCompleteShow(textbefore, len (textbehind))
 
3122
            else:
 
3123
                #call CallTips
 
3124
                cpos = pointavailpos
 
3125
                begpos = -1
 
3126
                while cpos > stoppos:
 
3127
                    if chr(self.GetCharAt(cpos)).isspace():
 
3128
                        begpos = cpos
 
3129
                        break
 
3130
                    cpos -= 1
 
3131
                if begpos == -1:
 
3132
                    begpos = cpos
 
3133
                ctips = self.GetTextRange (begpos, currpos)
 
3134
                ctindex = ctips.find ('(')
 
3135
                if ctindex != -1 and not self.CallTipActive():
 
3136
                    #insert calltip, if current pos is '(', otherwise show it only
 
3137
                    self.autoCallTipShow( ctips[:ctindex + 1], 
 
3138
                          self.GetCharAt(currpos - 1) == ord('(') and
 
3139
                              self.GetCurrentPos() == self.GetTextLength(),
 
3140
                          True )
 
3141
                
 
3142
 
 
3143
    def writeOut(self, text):
 
3144
        """Replacement for stdout."""
 
3145
        self.write(text,type='Output')
 
3146
        # TODO : FLUSH?? How to make this update real-time...
 
3147
 
 
3148
    def writeErr(self, text):
 
3149
        """Replacement for stderr."""
 
3150
        self.write(text,type='Error')
 
3151
 
 
3152
    def redirectStdin(self, redirect=True):
 
3153
        """If redirect is true then sys.stdin will come from the shell."""
 
3154
        if redirect:
 
3155
            sys.stdin = self.reader
 
3156
        else:
 
3157
            sys.stdin = self.stdin
 
3158
 
 
3159
    def redirectStdout(self, redirect=True):
 
3160
        """If redirect is true then sys.stdout will go to the shell."""
 
3161
        if redirect:
 
3162
            sys.stdout = PseudoFileOut(self.writeOut)
 
3163
        else:
 
3164
            sys.stdout = self.stdout
 
3165
 
 
3166
    def redirectStderr(self, redirect=True):
 
3167
        """If redirect is true then sys.stderr will go to the shell."""
 
3168
        if redirect:
 
3169
            sys.stderr = PseudoFileErr(self.writeErr)
 
3170
        else:
 
3171
            sys.stderr = self.stderr
 
3172
    
 
3173
    # Take a spashot of the WHOLE grouping slice (or slices)
 
3174
    # The argument s is either what got added or deleted
 
3175
    def UpdateUndoHistoryBefore(self,actionType,s,posStart,posEnd,
 
3176
                                forceNewAction=False):
 
3177
        uH=self.undoHistory
 
3178
        uI=self.undoIndex
 
3179
        
 
3180
        s=s.replace(os.linesep,'\n')
 
3181
        startLine=self.LineFromPosition(posStart)
 
3182
        
 
3183
        if actionType=='marker':
 
3184
            numLines = self.LineFromPosition(posEnd) - startLine
 
3185
        else:
 
3186
            numLines=s.count('\n')
 
3187
        
 
3188
        makeNewAction=forceNewAction
 
3189
        
 
3190
        if forceNewAction:
 
3191
            makeNewAction=True
 
3192
        elif self.undoIndex==-1:
 
3193
            makeNewAction=True
 
3194
        elif not uH[uI]['allowsAppend']:
 
3195
            makeNewAction=True
 
3196
        elif actionType!=uH[uI]['actionType']:
 
3197
            makeNewAction=True
 
3198
        elif actionType=='insert':
 
3199
            if posStart!=uH[uI]['posEnd']:
 
3200
                makeNewAction=True
 
3201
            else: # This is a continuation of the previous insert
 
3202
                uH[uI]['charList'] = uH[uI]['charList']+s
 
3203
                uH[uI]['posEnd']   = posEnd # posStart cannot move
 
3204
                uH[uI]['numLines'] = uH[uI]['numLines']+numLines
 
3205
        elif actionType=='delete':
 
3206
            # This is a forward continuation of the previous delete
 
3207
            if posStart==uH[uI]['posStart']:
 
3208
                uH[uI]['charList'] = uH[uI]['charList']+s
 
3209
                uH[uI]['posEnd'] = posEnd
 
3210
                uH[uI]['numLines'] = uH[uI]['numLines']+numLines
 
3211
            # This is a backward continuation of the previous delete
 
3212
            elif posEnd==uH[uI]['posStart']:
 
3213
                uH[uI]['charList'] = s+uH[uI]['charList']
 
3214
                uH[uI]['posStart'] = posStart
 
3215
                uH[uI]['startLine'] = startLine
 
3216
                uH[uI]['numLines'] = uH[uI]['numLines']+numLines
 
3217
            else:
 
3218
                makeNewAction=True
 
3219
            
 
3220
        elif actionType=='marker':
 
3221
            makeNewAction=True
 
3222
        else:
 
3223
            pass #print 'Unsupported Action Type!!'
 
3224
        
 
3225
        if makeNewAction:
 
3226
            del(self.undoHistory[uI+1:]) # remove actions after undoIndex
 
3227
            
 
3228
            uH.append({
 
3229
              'actionType' : actionType,    # Action type ('insert','delete','marker')
 
3230
              'allowsAppend': not forceNewAction, # Can action be joined with others?
 
3231
              'charList' : s,        # Character list
 
3232
              'posStart' : posStart, # Cursor poition at the start of the action
 
3233
              'posEnd' : posEnd,     # Cursor position at the end of the action
 
3234
              'startLine' : startLine,  # Start line number,
 
3235
              'numLines' : numLines,  # Number of newlines involved
 
3236
              'mBStart' : None,       # Starting line for markers BEFORE action
 
3237
              'mAStart' : None,       # Starting line for markers AFTER action
 
3238
              'markersBefore' : None, # [markers BEFORE action]
 
3239
              'markersAfter' : None   # [markers AFTER action]
 
3240
             })
 
3241
            
 
3242
            self.undoIndex+=1
 
3243
            
 
3244
            # Only update the before when starting a new action
 
3245
            start = startLine
 
3246
            if actionType=='insert':
 
3247
                end = start
 
3248
            else:
 
3249
                end = start + numLines
 
3250
            
 
3251
            # Update Marker Info
 
3252
            newStart=self.GetGroupingSlice(start)[0]
 
3253
            newEnd=self.GetGroupingSlice(end)[1]
 
3254
            self.undoHistory[self.undoIndex]['markersBefore'] = \
 
3255
                 [self.MarkerGet(i) for i in range(newStart,newEnd+1)]
 
3256
            self.undoHistory[self.undoIndex]['mBStart']=newStart
 
3257
        
 
3258
        self.doHistUpdate=True
 
3259
    
 
3260
    def UpdateUndoHistoryAfter(self): # s is either what got added or deleted
 
3261
        start = self.undoHistory[self.undoIndex]['startLine']
 
3262
        if self.undoHistory[self.undoIndex]['actionType']=='delete':
 
3263
            end = start
 
3264
        else:
 
3265
            end = start + self.undoHistory[self.undoIndex]['numLines']
 
3266
        
 
3267
        newStart=min(self.GetGroupingSlice(start)[0]-1, 0)
 
3268
        newEnd=max(self.GetGroupingSlice(end)[1]+1, self.GetLineCount()-1)
 
3269
        self.undoHistory[self.undoIndex]['markersAfter'] = \
 
3270
             [self.MarkerGet(i) for i in range(newStart,newEnd+1)]
 
3271
        self.undoHistory[self.undoIndex]['mAStart']=newStart
 
3272
        
 
3273
        self.doHistUpdate=False
 
3274
    
 
3275
    def Undo(self):
 
3276
        #ADD UNDO
 
3277
        #Skip undo if there are no actions...
 
3278
        if self.undoIndex==-1:
 
3279
            return
 
3280
        
 
3281
        uHI=self.undoHistory[self.undoIndex]
 
3282
        
 
3283
        if uHI['actionType'] in ['insert','delete']:
 
3284
            # This will perform the opposite of the action given
 
3285
            editwindow.EditWindow.Undo(self)
 
3286
        elif uHI['actionType']=='marker': # No text changed, don't pass to STC
 
3287
            pass
 
3288
        else:
 
3289
            #print 'Unsupported actionType in undoHistory!!'
 
3290
            return
 
3291
        
 
3292
        numLines=len(uHI['markersBefore'])
 
3293
        for i in range(numLines):
 
3294
            self.MarkerSet( uHI['mBStart']+i , uHI['markersBefore'][i] )
 
3295
        
 
3296
        self.undoIndex-=1
 
3297
    
 
3298
    def Redo(self):
 
3299
        #ADD UNDO
 
3300
        # First check to see if there are any redo operations available
 
3301
        # Note that for redo, undoIndex=-1 is a valid input
 
3302
        if self.undoIndex >= len(self.undoHistory)-1:
 
3303
            return
 
3304
        self.undoIndex+=1
 
3305
        uHI=self.undoHistory[self.undoIndex]
 
3306
        
 
3307
        if uHI['actionType'] in ['insert','delete']:
 
3308
            # This will re-perform the given action
 
3309
            editwindow.EditWindow.Redo(self)
 
3310
        elif uHI['actionType']=='marker': # No text changed, don't pass to STC
 
3311
            pass
 
3312
        else:
 
3313
            #print 'Unsupported actionType in undoHistory!!'
 
3314
            return
 
3315
        
 
3316
        numLines=len(uHI['markersAfter'])
 
3317
        for i in range(numLines):
 
3318
            self.MarkerSet( uHI['mAStart']+i , uHI['markersAfter'][i] )
 
3319
    
 
3320
    def EmptyUndoBuffer(self):
 
3321
        editwindow.EditWindow.EmptyUndoBuffer(self)
 
3322
        self.undoIndex=-1
 
3323
        self.undoHistory=[]
 
3324
        self.doHistUpdate=False
 
3325
    
 
3326
    def CanCut(self):
 
3327
        return self.CanEdit() and \
 
3328
               (self.GetSelectionStart() != self.GetSelectionEnd())
 
3329
    
 
3330
    def CanPaste(self):
 
3331
        """Return true if a paste should succeed."""
 
3332
        if self.CanEdit() and editwindow.EditWindow.CanPaste(self):
 
3333
            return True
 
3334
        else:
 
3335
            return False
 
3336
 
 
3337
    def CanEdit(self):
 
3338
        """Return true if editing should succeed."""
 
3339
        marker=self.MarkerGet(self.GetCurrentLine())
 
3340
        
 
3341
        if marker & OUTPUT_MASK:
 
3342
            return False
 
3343
        elif marker & INPUT_MASK:
 
3344
            if self.reader.isreading and not \
 
3345
                    (self.MarkerGet(self.GetCurrentLine()) & 1<<INPUT_READLINE ):
 
3346
                return False
 
3347
            start,end=self.GetIOSlice()
 
3348
            sliceStartPos=self.PositionFromLine(start)
 
3349
            sliceEndPos=self.GetLineEndPosition(end)
 
3350
            """Return true if text is selected and can be cut."""
 
3351
            if self.GetSelectionStart() == self.GetSelectionEnd():
 
3352
                return True
 
3353
            elif self.GetSelectionStart() != self.GetSelectionEnd() \
 
3354
                   and self.GetSelectionStart() >= sliceStartPos \
 
3355
                   and self.GetSelectionEnd() >= sliceStartPos \
 
3356
                   and self.GetSelectionStart() <= sliceEndPos \
 
3357
                   and self.GetSelectionEnd() <= sliceEndPos:
 
3358
                return True
 
3359
            else:
 
3360
                return False
 
3361
 
 
3362
    def Cut(self):
 
3363
        """Remove selection and place it on the clipboard."""
 
3364
        if self.CanCut() and self.CanCopy():
 
3365
            if self.AutoCompActive():
 
3366
                self.AutoCompCancel()
 
3367
            if self.CallTipActive():
 
3368
                self.CallTipCancel()
 
3369
            self.Copy()
 
3370
            self.ReplaceSelection('')
 
3371
 
 
3372
    def Copy(self):
 
3373
        """Copy selection and place it on the clipboard."""
 
3374
        if self.CanCopy():
 
3375
            ps1 = str(sys.ps1)
 
3376
            ps2 = str(sys.ps2)
 
3377
            command = self.GetSelectedText()
 
3378
            command = command.replace(os.linesep + ps2, os.linesep)
 
3379
            command = command.replace(os.linesep + ps1, os.linesep)
 
3380
            command = self.lstripPrompt(text=command)
 
3381
            data = wx.TextDataObject(command)
 
3382
            self._clip(data)
 
3383
 
 
3384
    def CopyWithPrompts(self):
 
3385
        """Copy selection, including prompts, and place it on the clipboard."""
 
3386
        if self.CanCopy():
 
3387
            command = self.GetSelectedText()
 
3388
            data = wx.TextDataObject(command)
 
3389
            self._clip(data)
 
3390
 
 
3391
    def CopyWithPromptsPrefixed(self):
 
3392
        """Copy selection, including prompts prefixed with four
 
3393
        spaces, and place it on the clipboard."""
 
3394
        if self.CanCopy():
 
3395
            command = self.GetSelectedText()
 
3396
            spaces = ' ' * 4
 
3397
            command = spaces + command.replace(os.linesep,
 
3398
                                               os.linesep + spaces)
 
3399
            data = wx.TextDataObject(command)
 
3400
            self._clip(data)
 
3401
 
 
3402
    def _clip(self, data):
 
3403
        if wx.TheClipboard.Open():
 
3404
            wx.TheClipboard.UsePrimarySelection(False)
 
3405
            wx.TheClipboard.SetData(data)
 
3406
            wx.TheClipboard.Flush()
 
3407
            wx.TheClipboard.Close()
 
3408
 
 
3409
    def Paste(self):
 
3410
        """Replace selection with clipboard contents."""
 
3411
        
 
3412
        #ADD UNDO
 
3413
        if self.CanPaste() and wx.TheClipboard.Open():
 
3414
            ps2 = str(sys.ps2)
 
3415
            if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)):
 
3416
                data = wx.TextDataObject()
 
3417
                if wx.TheClipboard.GetData(data):
 
3418
                    self.ReplaceSelection('')
 
3419
                    command = data.GetText()
 
3420
                    command = command.rstrip()
 
3421
                    command = self.fixLineEndings(command)
 
3422
                    command = self.lstripPrompt(text=command)
 
3423
                    # TODO : This is still useful... Add it back other places?
 
3424
                    command = command.replace(os.linesep + ps2, '\n')
 
3425
                    command = command.replace(os.linesep, '\n')
 
3426
                    #DNM--Don't use '... '
 
3427
                    command = command.replace('\n', os.linesep)# + ps2)
 
3428
                    
 
3429
                    cpos=self.GetCurrentPos()
 
3430
                    s=command
 
3431
                    self.UpdateUndoHistoryBefore('insert', s, cpos,
 
3432
                                            cpos+len(s), forceNewAction=True)
 
3433
                    self.write(s,type='Input')
 
3434
                    self.UpdateUndoHistoryAfter()
 
3435
                    
 
3436
                    # Makes paste -> type -> undo consistent with other STC apps
 
3437
                    self.ReplaceSelection('')
 
3438
            wx.TheClipboard.Close()
 
3439
    
 
3440
    
 
3441
    def PasteAndRun(self):
 
3442
        """Replace selection with clipboard contents, run commands."""
 
3443
        text = ''
 
3444
        if wx.TheClipboard.Open():
 
3445
            if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)):
 
3446
                data = wx.TextDataObject()
 
3447
                if wx.TheClipboard.GetData(data):
 
3448
                    text = data.GetText()
 
3449
            wx.TheClipboard.Close()
 
3450
        if text:
 
3451
            self.Execute(text)
 
3452
            
 
3453
    
 
3454
    def Execute(self, text):
 
3455
        """Replace selection with text and run commands."""
 
3456
        start,end=self.GetIOSlice()
 
3457
        startpos=self.PositionFromLine(start)
 
3458
        endpos=self.GetLineEndPosition(end)
 
3459
        
 
3460
        self.SetCurrentPos(endpos)
 
3461
        self.SetSelection(startpos, endpos)
 
3462
        self.ReplaceSelection('')
 
3463
        
 
3464
        hasSyntaxError=False
 
3465
        result = self.BreakTextIntoCommands(command)
 
3466
        if result[0] == None:
 
3467
            commands=[command]
 
3468
            hasSyntaxError=True
 
3469
        else:
 
3470
            commands=result
 
3471
        
 
3472
        for command in commands:
 
3473
            command = command.replace('\n', os.linesep)
 
3474
            self.write(command)
 
3475
            self.processLine()
 
3476
 
 
3477
    def wrap(self, wrap=True):
 
3478
        """Sets whether text is word wrapped."""
 
3479
        try:
 
3480
            self.SetWrapMode(wrap)
 
3481
        except AttributeError:
 
3482
            return 'Wrapping is not available in this version.'
 
3483
 
 
3484
    def zoom(self, points=0):
 
3485
        """Set the zoom level.
 
3486
 
 
3487
        This number of points is added to the size of all fonts.  It
 
3488
        may be positive to magnify or negative to reduce."""
 
3489
        self.SetZoom(points)
 
3490
 
 
3491
    def LoadSettings(self, config):
 
3492
        self.autoComplete = \
 
3493
                    config.ReadBool('Options/AutoComplete', True)
 
3494
        self.autoCompleteIncludeMagic = \
 
3495
                    config.ReadBool('Options/AutoCompleteIncludeMagic', True)
 
3496
        self.autoCompleteIncludeSingle = \
 
3497
                    config.ReadBool('Options/AutoCompleteIncludeSingle', True)
 
3498
        self.autoCompleteIncludeDouble = \
 
3499
                    config.ReadBool('Options/AutoCompleteIncludeDouble', True)
 
3500
        self.autoCallTip = \
 
3501
                    config.ReadBool('Options/AutoCallTip', True)
 
3502
        self.callTipInsert = \
 
3503
                    config.ReadBool('Options/CallTipInsert', True)
 
3504
        
 
3505
        self.SetWrapMode(config.ReadBool('View/WrapMode', True))
 
3506
 
 
3507
        self.lineNumbers = \
 
3508
                    config.ReadBool('View/ShowLineNumbers', True)
 
3509
        self.setDisplayLineNumbers (self.lineNumbers)
 
3510
        zoom = config.ReadInt('View/Zoom/Shell', -99)
 
3511
        if zoom != -99:
 
3512
            self.SetZoom(zoom)
 
3513
 
 
3514
 
 
3515
    
 
3516
    def SaveSettings(self, config):
 
3517
        config.WriteBool('Options/AutoComplete', self.autoComplete)
 
3518
        config.WriteBool('Options/AutoCompleteIncludeMagic',
 
3519
                                            self.autoCompleteIncludeMagic)
 
3520
        config.WriteBool('Options/AutoCompleteIncludeSingle',
 
3521
                                            self.autoCompleteIncludeSingle)
 
3522
        config.WriteBool('Options/AutoCompleteIncludeDouble',
 
3523
                                            self.autoCompleteIncludeDouble)
 
3524
        config.WriteBool('Options/AutoCallTip', self.autoCallTip)
 
3525
        config.WriteBool('Options/CallTipInsert', self.callTipInsert)
 
3526
        config.WriteBool('View/WrapMode', self.GetWrapMode())
 
3527
        config.WriteBool('View/ShowLineNumbers', self.lineNumbers)
 
3528
        config.WriteInt('View/Zoom/Shell', self.GetZoom())
 
3529
 
 
3530
    def GetContextMenu(self):
 
3531
        """
 
3532
            Create and return a context menu for the slices shell.
 
3533
            This is used instead of the scintilla default menu
 
3534
            in order to correctly respect our immutable buffer.
 
3535
        """
 
3536
        menu = wx.Menu()
 
3537
        menu.Append(wx.ID_UNDO, "Undo")
 
3538
        menu.Append(wx.ID_REDO, "Redo")
 
3539
        
 
3540
        menu.AppendSeparator()
 
3541
        
 
3542
        menu.Append(wx.ID_CUT, "Cut")
 
3543
        menu.Append(wx.ID_COPY, "Copy")
 
3544
        menu.Append(frame.ID_COPY_PLUS, "Copy Plus")
 
3545
        menu.Append(wx.ID_PASTE, "Paste")
 
3546
        menu.Append(frame.ID_PASTE_PLUS, "Paste Plus")
 
3547
        menu.Append(wx.ID_CLEAR, "Clear")
 
3548
        
 
3549
        menu.AppendSeparator()
 
3550
        
 
3551
        menu.Append(wx.ID_SELECTALL, "Select All")
 
3552
        return menu
 
3553
        
 
3554
    def OnContextMenu(self, evt):
 
3555
        menu = self.GetContextMenu()
 
3556
        self.PopupMenu(menu)
 
3557
        
 
3558
    def OnUpdateUI(self, evt):
 
3559
        id = evt.Id
 
3560
        if id in (wx.ID_CUT, wx.ID_CLEAR):
 
3561
            evt.Enable(self.CanCut())
 
3562
        elif id in (wx.ID_COPY, frame.ID_COPY_PLUS):
 
3563
            evt.Enable(self.CanCopy())
 
3564
        elif id in (wx.ID_PASTE, frame.ID_PASTE_PLUS):
 
3565
            evt.Enable(self.CanPaste())
 
3566
        elif id == wx.ID_UNDO:
 
3567
            evt.Enable(self.CanUndo())
 
3568
        elif id == wx.ID_REDO:
 
3569
            evt.Enable(self.CanRedo())
 
3570
 
 
3571
    def LoadPySlicesFile(self,fid):
 
3572
        invalidFileString = 'Not a valid input format'
 
3573
        lineCount=0
 
3574
        groupingStartLines=[0]
 
3575
        ioStartLines=[0]
 
3576
        ioStartTypes=[]
 
3577
        removeComment=False
 
3578
        
 
3579
        # Read the initial three (or four) lines that have version and marker information
 
3580
        line=fid.readline()
 
3581
        if line == usrBinEnvPythonText:
 
3582
            line=fid.readline() # Add the option to place #!/usr/bin/env python at the top
 
3583
        if line not in pyslicesFormatHeaderText:  print invalidFileString ; return
 
3584
        line=fid.readline()
 
3585
        if line != groupingStartText:  print invalidFileString ; return
 
3586
        line=fid.readline()
 
3587
        if line == inputStartText:      ioStartTypes.append('input');removeComment=False
 
3588
        elif line == outputStartText:   ioStartTypes.append('output');removeComment=True
 
3589
        else:  print invalidFileString ; return
 
3590
        
 
3591
        self.ClearAll()
 
3592
        
 
3593
        # Write the file's text to the text area
 
3594
        # Capture Marker information to
 
3595
        for i in fid:
 
3596
            if i==groupingStartText:
 
3597
                groupingStartLines.append(lineCount)
 
3598
            elif i==inputStartText:
 
3599
                ioStartLines.append(lineCount)
 
3600
                ioStartTypes.append('input')
 
3601
                removeComment=False
 
3602
            elif i==outputStartText:
 
3603
                ioStartLines.append(lineCount)
 
3604
                ioStartTypes.append('output')
 
3605
                removeComment=True
 
3606
            else:
 
3607
                if removeComment:   w=i[1:].replace(os.linesep,'\n')
 
3608
                else:               w=i.replace(os.linesep,'\n')
 
3609
                self.write(w,'Input',silent=True)
 
3610
                lineCount+=1
 
3611
        
 
3612
        if w[-1]=='\n':
 
3613
            lineCount+=1
 
3614
        
 
3615
        for i in range(lineCount+1):
 
3616
            self.clearGroupingMarkers(i)
 
3617
            self.clearIOMarkers(i)
 
3618
            
 
3619
            doMiddle=False
 
3620
            doEnd=False
 
3621
            if groupingStartLines!=[]:
 
3622
                if i == groupingStartLines[0]:
 
3623
                    self.MarkerAdd(i,GROUPING_START)
 
3624
                    del groupingStartLines[0]
 
3625
                elif i+1 == groupingStartLines[0]:
 
3626
                    doEnd=True
 
3627
                else:
 
3628
                    doMiddle=True
 
3629
            elif i==lineCount-1:
 
3630
                doEnd=True
 
3631
            else:
 
3632
                doMiddle=True
 
3633
            
 
3634
            if doMiddle:
 
3635
                self.MarkerAdd(i,GROUPING_MIDDLE)
 
3636
            elif doEnd:
 
3637
                self.MarkerAdd(i,GROUPING_END)
 
3638
            
 
3639
            doMiddle=False
 
3640
            doEnd=False
 
3641
            if ioStartLines!=[]:
 
3642
                if i == ioStartLines[0]:
 
3643
                    # Delete the old ioStartTypes (keep the current copy for later use)
 
3644
                    if i>0: del ioStartTypes[0]
 
3645
                    
 
3646
                    if ioStartTypes[0]=='input':
 
3647
                        self.MarkerAdd(i,INPUT_START)
 
3648
                    elif ioStartTypes[0]=='output':
 
3649
                        self.MarkerAdd(i,OUTPUT_START)
 
3650
                        self.MarkerAdd(i,OUTPUT_BG)
 
3651
                    else:
 
3652
                        #print 'Invalid Type!';
 
3653
                        return
 
3654
                    
 
3655
                    # Only delete markers we are totally finished with...
 
3656
                    # Keep one more "StartTypes" than "StartLines"
 
3657
                    del ioStartLines[0]
 
3658
                elif i+1 == ioStartLines[0]:
 
3659
                    doEnd=True
 
3660
                else:
 
3661
                    doMiddle=True
 
3662
            elif i==lineCount-1:
 
3663
                doEnd=True
 
3664
            else:
 
3665
                doMiddle=True
 
3666
            
 
3667
            if doMiddle:
 
3668
                if ioStartTypes[0]=='input':
 
3669
                    self.MarkerAdd(i,INPUT_MIDDLE)
 
3670
                elif ioStartTypes[0]=='output':
 
3671
                    self.MarkerAdd(i,OUTPUT_MIDDLE)
 
3672
                    self.MarkerAdd(i,OUTPUT_BG)
 
3673
                else:
 
3674
                    #print 'Invalid Type!';
 
3675
                    return
 
3676
            elif doEnd:
 
3677
                if ioStartTypes[0]=='input':
 
3678
                    self.MarkerAdd(i,INPUT_END)
 
3679
                elif ioStartTypes[0]=='output':
 
3680
                    self.MarkerAdd(i,OUTPUT_END)
 
3681
                    self.MarkerAdd(i,OUTPUT_BG)
 
3682
                else:
 
3683
                    #print 'Invalid Type!';
 
3684
                    return
 
3685
                
 
3686
        self.EmptyUndoBuffer() # maybe not?
 
3687
 
 
3688
    
 
3689
    def SavePySlicesFile(self,fid):
 
3690
        addComment=False
 
3691
        fid.write(usrBinEnvPythonText.replace('\n',os.linesep))
 
3692
        fid.write(pyslicesFormatHeaderText[-1].replace('\n',os.linesep))
 
3693
        for i in range(self.GetLineCount()):
 
3694
            markers=self.MarkerGet(i)
 
3695
            if markers & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ):
 
3696
                fid.write(groupingStartText.replace('\n',os.linesep))
 
3697
            if markers & ( 1<<INPUT_START | 1<<INPUT_START_FOLDED ):
 
3698
                fid.write(inputStartText.replace('\n',os.linesep))
 
3699
                addComment=False
 
3700
            if markers & ( 1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED ):
 
3701
                fid.write(outputStartText.replace('\n',os.linesep))
 
3702
                addComment=True
 
3703
            if addComment: fid.write('#')
 
3704
            fid.write(self.GetLine(i).replace('\n',os.linesep))
 
3705
    
 
3706
    # FIX ME!!
 
3707
    def LoadPyFileAsSlice(self,fid):
 
3708
        curpos=self.GetCurrentPos()
 
3709
        start,end = self.GetGroupingSlice()
 
3710
        
 
3711
        endpos=self.GetLineEndPosition(end)
 
3712
        self.SetCurrentPos(endpos)
 
3713
        self.SetSelection(endpos, endpos)
 
3714
        
 
3715
        text='\n'+fid.read()
 
3716
        self.write(text,'Input')
 
3717
        newpos=self.GetCurrentPos()
 
3718
        
 
3719
        self.SetCurrentPos(curpos)
 
3720
        self.SetSelection(curpos,curpos)
 
3721
        self.SplitSlice()
 
3722
        #self.SetCurrentPos(newpos)
 
3723
        #self.SetSelection(newpos,newpos)
 
3724
    
 
3725
    def hasChanged(self):
 
3726
        """Return True if contents have changed."""
 
3727
        return self.GetModify() or self.NeedsCheckForSave
 
3728
        
 
3729
        
 
3730
        
 
3731
## NOTE: The DnD of file names is disabled until we can figure out how
 
3732
## best to still allow DnD of text.
 
3733
 
 
3734
## #seb : File drag and drop
 
3735
## class FileDropTarget(wx.FileDropTarget):
 
3736
##     def __init__(self, obj):
 
3737
##         wx.FileDropTarget.__init__(self)
 
3738
##         self.obj = obj
 
3739
##     def OnDropFiles(self, x, y, filenames):
 
3740
##         if len(filenames) == 1:
 
3741
##             txt = 'r\"%s\"' % filenames[0]
 
3742
##         else:
 
3743
##             txt = '( '
 
3744
##             for f in filenames:
 
3745
##                 txt += 'r\"%s\" , ' % f
 
3746
##             txt += ')'
 
3747
##         self.obj.AppendText(txt)
 
3748
##         pos = self.obj.GetCurrentPos()
 
3749
##         self.obj.SetCurrentPos( pos )
 
3750
##         self.obj.SetSelection( pos, pos )
 
3751
 
 
3752
 
 
3753
 
 
3754
## class TextAndFileDropTarget(wx.DropTarget):
 
3755
##     def __init__(self, sliceshell):
 
3756
##         wx.DropTarget.__init__(self)
 
3757
##         self.sliceshell = sliceshell
 
3758
##         self.compdo = wx.DataObjectComposite()
 
3759
##         self.textdo = wx.TextDataObject()
 
3760
##         self.filedo = wx.FileDataObject()
 
3761
##         self.compdo.Add(self.textdo)
 
3762
##         self.compdo.Add(self.filedo, True)
 
3763
        
 
3764
##         self.SetDataObject(self.compdo)
 
3765
                
 
3766
##     def OnDrop(self, x, y):
 
3767
##         return True
 
3768
    
 
3769
##     def OnData(self, x, y, result):
 
3770
##         self.GetData()
 
3771
##         if self.textdo.GetTextLength() > 1:
 
3772
##             text = self.textdo.GetText()
 
3773
##             # *** Do somethign with the dragged text here...
 
3774
##             self.textdo.SetText('')
 
3775
##         else:
 
3776
##             filenames = str(self.filename.GetFilenames())
 
3777
##             if len(filenames) == 1:
 
3778
##                 txt = 'r\"%s\"' % filenames[0]
 
3779
##             else:
 
3780
##                 txt = '( '
 
3781
##                 for f in filenames:
 
3782
##                     txt += 'r\"%s\" , ' % f
 
3783
##                 txt += ')'
 
3784
##             self.sliceshell.AppendText(txt)
 
3785
##             pos = self.sliceshell.GetCurrentPos()
 
3786
##             self.sliceshell.SetCurrentPos( pos )
 
3787
##             self.sliceshell.SetSelection( pos, pos )
 
3788
 
 
3789
##         return result