]> git.saurik.com Git - wxWidgets.git/blame - wxPython/wx/py/shell.py
moved vararg CRT functions wrappers to a new wxcrtvararg.h header
[wxWidgets.git] / wxPython / wx / py / shell.py
CommitLineData
d14a1e28
RD
1"""Shell is an interactive text control in which a user types in
2commands to be sent to the interpreter. This particular shell is
3based on wxPython's wxStyledTextCtrl.
1fded56b 4
d14a1e28 5Sponsored by Orbtech - Your source for Python programming expertise."""
1fded56b 6
d14a1e28
RD
7__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
8__cvsid__ = "$Id$"
9__revision__ = "$Revision$"[11:-2]
10
d14a1e28
RD
11import wx
12from wx import stc
13
14import keyword
15import os
16import sys
17import time
18
19from buffer import Buffer
20import dispatcher
21import editwindow
22import frame
23from pseudo import PseudoFileIn
24from pseudo import PseudoFileOut
25from pseudo import PseudoFileErr
26from version import VERSION
27
d14a1e28
RD
28sys.ps3 = '<-- ' # Input prompt.
29
30NAVKEYS = (wx.WXK_END, wx.WXK_LEFT, wx.WXK_RIGHT,
31 wx.WXK_UP, wx.WXK_DOWN, wx.WXK_PRIOR, wx.WXK_NEXT)
32
33
02b800ce 34class ShellFrame(frame.Frame, frame.ShellFrameMixin):
d14a1e28
RD
35 """Frame containing the shell component."""
36
37 name = 'Shell Frame'
38 revision = __revision__
39
40 def __init__(self, parent=None, id=-1, title='PyShell',
41 pos=wx.DefaultPosition, size=wx.DefaultSize,
42 style=wx.DEFAULT_FRAME_STYLE, locals=None,
02b800ce
RD
43 InterpClass=None,
44 config=None, dataDir=None,
45 *args, **kwds):
d14a1e28
RD
46 """Create ShellFrame instance."""
47 frame.Frame.__init__(self, parent, id, title, pos, size, style)
02b800ce
RD
48 frame.ShellFrameMixin.__init__(self, config, dataDir)
49
50 if size == wx.DefaultSize:
51 self.SetSize((750, 525))
52
d14a1e28 53 intro = 'PyShell %s - The Flakiest Python Shell' % VERSION
d14a1e28
RD
54 self.SetStatusText(intro.replace('\n', ', '))
55 self.shell = Shell(parent=self, id=-1, introText=intro,
56 locals=locals, InterpClass=InterpClass,
02b800ce
RD
57 startupScript=self.startupScript,
58 execStartupScript=self.execStartupScript,
d14a1e28 59 *args, **kwds)
02b800ce 60
d14a1e28
RD
61 # Override the shell so that status messages go to the status bar.
62 self.shell.setStatusText = self.SetStatusText
63
02b800ce
RD
64 self.shell.SetFocus()
65 self.LoadSettings()
66
67
d14a1e28
RD
68 def OnClose(self, event):
69 """Event handler for closing."""
70 # This isn't working the way I want, but I'll leave it for now.
71 if self.shell.waiting:
72 if event.CanVeto():
73 event.Veto(True)
74 else:
02b800ce 75 self.SaveSettings()
d14a1e28
RD
76 self.shell.destroy()
77 self.Destroy()
78
79 def OnAbout(self, event):
80 """Display an About window."""
81 title = 'About PyShell'
82 text = 'PyShell %s\n\n' % VERSION + \
83 'Yet another Python shell, only flakier.\n\n' + \
84 'Half-baked by Patrick K. O\'Brien,\n' + \
85 'the other half is still in the oven.\n\n' + \
86 'Shell Revision: %s\n' % self.shell.revision + \
87 'Interpreter Revision: %s\n\n' % self.shell.interp.revision + \
f2f8a5fc 88 'Platform: %s\n' % sys.platform + \
d14a1e28
RD
89 'Python Version: %s\n' % sys.version.split()[0] + \
90 'wxPython Version: %s\n' % wx.VERSION_STRING + \
f2f8a5fc 91 ('\t(%s)\n' % ", ".join(wx.PlatformInfo[1:]))
d14a1e28
RD
92 dialog = wx.MessageDialog(self, text, title,
93 wx.OK | wx.ICON_INFORMATION)
94 dialog.ShowModal()
95 dialog.Destroy()
96
97
4617be08
RD
98 def OnHelp(self, event):
99 """Show a help dialog."""
100 frame.ShellFrameMixin.OnHelp(self, event)
101
102
02b800ce
RD
103 def LoadSettings(self):
104 if self.config is not None:
105 frame.ShellFrameMixin.LoadSettings(self)
106 frame.Frame.LoadSettings(self, self.config)
107 self.shell.LoadSettings(self.config)
d14a1e28 108
c0ab3f7f 109 def SaveSettings(self, force=False):
02b800ce
RD
110 if self.config is not None:
111 frame.ShellFrameMixin.SaveSettings(self)
c0ab3f7f 112 if self.autoSaveSettings or force:
02b800ce
RD
113 frame.Frame.SaveSettings(self, self.config)
114 self.shell.SaveSettings(self.config)
d14a1e28 115
02b800ce
RD
116 def DoSaveSettings(self):
117 if self.config is not None:
c0ab3f7f 118 self.SaveSettings(force=True)
02b800ce
RD
119 self.config.Flush()
120
d14a1e28 121
02b800ce
RD
122
123
124HELP_TEXT = """\
d14a1e28
RD
125* Key bindings:
126Home Go to the beginning of the command or line.
127Shift+Home Select to the beginning of the command or line.
128Shift+End Select to the end of the line.
129End Go to the end of the line.
130Ctrl+C Copy selected text, removing prompts.
131Ctrl+Shift+C Copy selected text, retaining prompts.
02b800ce 132Alt+C Copy to the clipboard, including prefixed prompts.
d14a1e28
RD
133Ctrl+X Cut selected text.
134Ctrl+V Paste from clipboard.
135Ctrl+Shift+V Paste and run multiple commands from clipboard.
136Ctrl+Up Arrow Retrieve Previous History item.
137Alt+P Retrieve Previous History item.
138Ctrl+Down Arrow Retrieve Next History item.
139Alt+N Retrieve Next History item.
140Shift+Up Arrow Insert Previous History item.
141Shift+Down Arrow Insert Next History item.
142F8 Command-completion of History item.
143 (Type a few characters of a previous command and press F8.)
144Ctrl+Enter Insert new line into multiline command.
145Ctrl+] Increase font size.
146Ctrl+[ Decrease font size.
147Ctrl+= Default font size.
02b800ce
RD
148Ctrl-Space Show Auto Completion.
149Ctrl-Alt-Space Show Call Tip.
02b800ce 150Shift+Enter Complete Text from History.
e773f79b
RD
151Ctrl+F Search
152F3 Search next
02b800ce
RD
153Ctrl+H "hide" lines containing selection / "unhide"
154F12 on/off "free-edit" mode
d14a1e28
RD
155"""
156
02b800ce
RD
157class ShellFacade:
158 """Simplified interface to all shell-related functionality.
159
160 This is a semi-transparent facade, in that all attributes of other
161 are accessible, even though only some are visible to the user."""
162
163 name = 'Shell Interface'
164 revision = __revision__
165
166 def __init__(self, other):
167 """Create a ShellFacade instance."""
168 d = self.__dict__
169 d['other'] = other
170 d['helpText'] = HELP_TEXT
c15cc56f 171 d['this'] = other.this
02b800ce 172
d14a1e28
RD
173 def help(self):
174 """Display some useful information about how to use the shell."""
175 self.write(self.helpText)
176
177 def __getattr__(self, name):
178 if hasattr(self.other, name):
179 return getattr(self.other, name)
180 else:
181 raise AttributeError, name
182
183 def __setattr__(self, name, value):
184 if self.__dict__.has_key(name):
185 self.__dict__[name] = value
186 elif hasattr(self.other, name):
187 setattr(self.other, name, value)
188 else:
189 raise AttributeError, name
190
191 def _getAttributeNames(self):
192 """Return list of magic attributes to extend introspection."""
193 list = [
194 'about',
195 'ask',
196 'autoCallTip',
197 'autoComplete',
d351525a 198 'autoCompleteAutoHide',
d14a1e28
RD
199 'autoCompleteCaseInsensitive',
200 'autoCompleteIncludeDouble',
201 'autoCompleteIncludeMagic',
202 'autoCompleteIncludeSingle',
02b800ce 203 'callTipInsert',
d14a1e28
RD
204 'clear',
205 'pause',
206 'prompt',
207 'quit',
208 'redirectStderr',
209 'redirectStdin',
210 'redirectStdout',
211 'run',
212 'runfile',
213 'wrap',
214 'zoom',
215 ]
216 list.sort()
217 return list
218
219
02b800ce 220
d14a1e28
RD
221class Shell(editwindow.EditWindow):
222 """Shell based on StyledTextCtrl."""
223
224 name = 'Shell'
225 revision = __revision__
226
227 def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
228 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
02b800ce
RD
229 introText='', locals=None, InterpClass=None,
230 startupScript=None, execStartupScript=True,
231 *args, **kwds):
d14a1e28
RD
232 """Create Shell instance."""
233 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
234 self.wrap()
235 if locals is None:
a47c63ba
PB
236 import __main__
237 locals = __main__.__dict__
02b800ce 238
d14a1e28
RD
239 # Grab these so they can be restored by self.redirect* methods.
240 self.stdin = sys.stdin
241 self.stdout = sys.stdout
242 self.stderr = sys.stderr
02b800ce 243
d14a1e28
RD
244 # Import a default interpreter class if one isn't provided.
245 if InterpClass == None:
246 from interpreter import Interpreter
247 else:
248 Interpreter = InterpClass
02b800ce 249
d14a1e28
RD
250 # Create a replacement for stdin.
251 self.reader = PseudoFileIn(self.readline, self.readlines)
252 self.reader.input = ''
253 self.reader.isreading = False
02b800ce 254
d14a1e28
RD
255 # Set up the interpreter.
256 self.interp = Interpreter(locals=locals,
257 rawin=self.raw_input,
258 stdin=self.reader,
259 stdout=PseudoFileOut(self.writeOut),
260 stderr=PseudoFileErr(self.writeErr),
261 *args, **kwds)
02b800ce 262
d14a1e28
RD
263 # Set up the buffer.
264 self.buffer = Buffer()
02b800ce 265
d14a1e28
RD
266 # Find out for which keycodes the interpreter will autocomplete.
267 self.autoCompleteKeys = self.interp.getAutoCompleteKeys()
02b800ce 268
d14a1e28
RD
269 # Keep track of the last non-continuation prompt positions.
270 self.promptPosStart = 0
271 self.promptPosEnd = 0
02b800ce 272
d14a1e28
RD
273 # Keep track of multi-line commands.
274 self.more = False
02b800ce 275
d14a1e28
RD
276 # Create the command history. Commands are added into the
277 # front of the list (ie. at index 0) as they are entered.
278 # self.historyIndex is the current position in the history; it
279 # gets incremented as you retrieve the previous command,
280 # decremented as you retrieve the next, and reset when you hit
281 # Enter. self.historyIndex == -1 means you're on the current
282 # command, not in the history.
283 self.history = []
284 self.historyIndex = -1
02b800ce
RD
285
286 #seb add mode for "free edit"
287 self.noteMode = 0
288 self.MarkerDefine(0,stc.STC_MARK_ROUNDRECT) # marker for hidden
289 self.searchTxt = ""
290
d14a1e28 291 # Assign handlers for keyboard events.
02b800ce
RD
292 self.Bind(wx.EVT_CHAR, self.OnChar)
293 self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
294
d14a1e28
RD
295 # Assign handler for idle time.
296 self.waiting = False
02b800ce
RD
297 self.Bind(wx.EVT_IDLE, self.OnIdle)
298
d14a1e28
RD
299 # Display the introductory banner information.
300 self.showIntro(introText)
02b800ce 301
d14a1e28
RD
302 # Assign some pseudo keywords to the interpreter's namespace.
303 self.setBuiltinKeywords()
02b800ce 304
d14a1e28
RD
305 # Add 'shell' to the interpreter's local namespace.
306 self.setLocalShell()
02b800ce
RD
307
308 ## NOTE: See note at bottom of this file...
309 ## #seb: File drag and drop
310 ## self.SetDropTarget( FileDropTarget(self) )
311
d14a1e28
RD
312 # Do this last so the user has complete control over their
313 # environment. They can override anything they want.
02b800ce
RD
314 if execStartupScript:
315 if startupScript is None:
316 startupScript = os.environ.get('PYTHONSTARTUP')
317 self.execStartupScript(startupScript)
318 else:
319 self.prompt()
320
d14a1e28
RD
321 wx.CallAfter(self.ScrollToLine, 0)
322
02b800ce 323
e773f79b
RD
324 def clearHistory(self):
325 self.history = []
326 self.historyIndex = -1
327 dispatcher.send(signal="Shell.clearHistory")
328
02b800ce 329
d14a1e28
RD
330 def destroy(self):
331 del self.interp
332
333 def setFocus(self):
334 """Set focus to the shell."""
335 self.SetFocus()
336
337 def OnIdle(self, event):
338 """Free the CPU to do other things."""
339 if self.waiting:
340 time.sleep(0.05)
341 event.Skip()
342
343 def showIntro(self, text=''):
344 """Display introductory text in the shell."""
345 if text:
d14a1e28
RD
346 self.write(text)
347 try:
61071e61
RD
348 if self.interp.introText:
349 if text and not text.endswith(os.linesep):
350 self.write(os.linesep)
351 self.write(self.interp.introText)
d14a1e28
RD
352 except AttributeError:
353 pass
354
355 def setBuiltinKeywords(self):
356 """Create pseudo keywords as part of builtins.
357
9f4cc34f 358 This sets "close", "exit" and "quit" to a helpful string.
d14a1e28
RD
359 """
360 import __builtin__
361 __builtin__.close = __builtin__.exit = __builtin__.quit = \
362 'Click on the close button to leave the application.'
363
02b800ce 364
d14a1e28
RD
365 def quit(self):
366 """Quit the application."""
d14a1e28 367 # XXX Good enough for now but later we want to send a close event.
d14a1e28
RD
368 # In the close event handler we can make sure they want to
369 # quit. Other applications, like PythonCard, may choose to
370 # hide rather than quit so we should just post the event and
371 # let the surrounding app decide what it wants to do.
372 self.write('Click on the close button to leave the application.')
373
02b800ce 374
d14a1e28
RD
375 def setLocalShell(self):
376 """Add 'shell' to locals as reference to ShellFacade instance."""
377 self.interp.locals['shell'] = ShellFacade(other=self)
378
02b800ce 379
d14a1e28
RD
380 def execStartupScript(self, startupScript):
381 """Execute the user's PYTHONSTARTUP script if they have one."""
382 if startupScript and os.path.isfile(startupScript):
383 text = 'Startup script executed: ' + startupScript
384 self.push('print %r; execfile(%r)' % (text, startupScript))
02b800ce 385 self.interp.startupScript = startupScript
d14a1e28
RD
386 else:
387 self.push('')
388
02b800ce 389
d14a1e28
RD
390 def about(self):
391 """Display information about Py."""
392 text = """
393Author: %r
394Py Version: %s
395Py Shell Revision: %s
396Py Interpreter Revision: %s
397Python Version: %s
398wxPython Version: %s
02b800ce 399wxPython PlatformInfo: %s
d14a1e28
RD
400Platform: %s""" % \
401 (__author__, VERSION, self.revision, self.interp.revision,
02b800ce
RD
402 sys.version.split()[0], wx.VERSION_STRING, str(wx.PlatformInfo),
403 sys.platform)
d14a1e28
RD
404 self.write(text.strip())
405
02b800ce 406
d14a1e28
RD
407 def OnChar(self, event):
408 """Keypress event handler.
409
410 Only receives an event if OnKeyDown calls event.Skip() for the
411 corresponding event."""
412
02b800ce
RD
413 if self.noteMode:
414 event.Skip()
415 return
416
d14a1e28
RD
417 # Prevent modification of previously submitted
418 # commands/responses.
419 if not self.CanEdit():
420 return
a3cee65e 421 key = event.GetKeyCode()
d14a1e28
RD
422 currpos = self.GetCurrentPos()
423 stoppos = self.promptPosEnd
424 # Return (Enter) needs to be ignored in this handler.
e773f79b 425 if key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]:
d14a1e28
RD
426 pass
427 elif key in self.autoCompleteKeys:
428 # Usually the dot (period) key activates auto completion.
429 # Get the command between the prompt and the cursor. Add
430 # the autocomplete character to the end of the command.
431 if self.AutoCompActive():
432 self.AutoCompCancel()
433 command = self.GetTextRange(stoppos, currpos) + chr(key)
434 self.write(chr(key))
435 if self.autoComplete:
436 self.autoCompleteShow(command)
437 elif key == ord('('):
438 # The left paren activates a call tip and cancels an
439 # active auto completion.
440 if self.AutoCompActive():
441 self.AutoCompCancel()
442 # Get the command between the prompt and the cursor. Add
443 # the '(' to the end of the command.
444 self.ReplaceSelection('')
445 command = self.GetTextRange(stoppos, currpos) + '('
446 self.write('(')
02b800ce 447 self.autoCallTipShow(command, self.GetCurrentPos() == self.GetTextLength())
d14a1e28
RD
448 else:
449 # Allow the normal event handling to take place.
450 event.Skip()
451
02b800ce 452
d14a1e28
RD
453 def OnKeyDown(self, event):
454 """Key down event handler."""
455
a3cee65e 456 key = event.GetKeyCode()
d14a1e28
RD
457 # If the auto-complete window is up let it do its thing.
458 if self.AutoCompActive():
459 event.Skip()
460 return
486afba9 461
d14a1e28
RD
462 # Prevent modification of previously submitted
463 # commands/responses.
464 controlDown = event.ControlDown()
465 altDown = event.AltDown()
466 shiftDown = event.ShiftDown()
467 currpos = self.GetCurrentPos()
468 endpos = self.GetTextLength()
469 selecting = self.GetSelectionStart() != self.GetSelectionEnd()
02b800ce 470
486afba9 471 if controlDown and shiftDown and key in (ord('F'), ord('f')):
02b800ce
RD
472 li = self.GetCurrentLine()
473 m = self.MarkerGet(li)
474 if m & 1<<0:
475 startP = self.PositionFromLine(li)
476 self.MarkerDelete(li, 0)
477 maxli = self.GetLineCount()
478 li += 1 # li stayed visible as header-line
479 li0 = li
480 while li<maxli and self.GetLineVisible(li) == 0:
481 li += 1
482 endP = self.GetLineEndPosition(li-1)
483 self.ShowLines(li0, li-1)
484 self.SetSelection( startP, endP ) # select reappearing text to allow "hide again"
485 return
486 startP,endP = self.GetSelection()
487 endP-=1
488 startL,endL = self.LineFromPosition(startP), self.LineFromPosition(endP)
489
490 if endL == self.LineFromPosition(self.promptPosEnd): # never hide last prompt
491 endL -= 1
492
493 m = self.MarkerGet(startL)
494 self.MarkerAdd(startL, 0)
495 self.HideLines(startL+1,endL)
496 self.SetCurrentPos( startP ) # to ensure caret stays visible !
497
498 if key == wx.WXK_F12: #seb
499 if self.noteMode:
500 # self.promptPosStart not used anyway - or ?
501 self.promptPosEnd = self.PositionFromLine( self.GetLineCount()-1 ) + len(str(sys.ps1))
502 self.GotoLine(self.GetLineCount())
503 self.GotoPos(self.promptPosEnd)
504 self.prompt() #make sure we have a prompt
505 self.SetCaretForeground("black")
506 self.SetCaretWidth(1) #default
507 self.SetCaretPeriod(500) #default
508 else:
509 self.SetCaretForeground("red")
510 self.SetCaretWidth(4)
511 self.SetCaretPeriod(0) #steady
512
513 self.noteMode = not self.noteMode
514 return
515 if self.noteMode:
516 event.Skip()
517 return
518
d14a1e28
RD
519 # Return (Enter) is used to submit a command to the
520 # interpreter.
e773f79b 521 if (not controlDown and not shiftDown and not altDown) and key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]:
d14a1e28
RD
522 if self.CallTipActive():
523 self.CallTipCancel()
524 self.processLine()
486afba9
RD
525
526 # Complete Text (from already typed words)
e773f79b 527 elif shiftDown and key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]:
02b800ce 528 self.OnShowCompHistory()
486afba9
RD
529
530 # Ctrl+Return (Ctrl+Enter) is used to insert a line break.
e773f79b 531 elif controlDown and key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]:
d14a1e28
RD
532 if self.CallTipActive():
533 self.CallTipCancel()
534 if currpos == endpos:
535 self.processLine()
536 else:
537 self.insertLineBreak()
486afba9 538
d14a1e28
RD
539 # Let Ctrl-Alt-* get handled normally.
540 elif controlDown and altDown:
541 event.Skip()
486afba9 542
d14a1e28
RD
543 # Clear the current, unexecuted command.
544 elif key == wx.WXK_ESCAPE:
545 if self.CallTipActive():
546 event.Skip()
547 else:
548 self.clearCommand()
486afba9 549
1ad0de84
RD
550 # Clear the current command
551 elif key == wx.WXK_BACK and controlDown and shiftDown:
552 self.clearCommand()
553
d14a1e28 554 # Increase font size.
486afba9 555 elif controlDown and key in (ord(']'), wx.WXK_NUMPAD_ADD):
d14a1e28 556 dispatcher.send(signal='FontIncrease')
486afba9 557
d14a1e28 558 # Decrease font size.
486afba9 559 elif controlDown and key in (ord('['), wx.WXK_NUMPAD_SUBTRACT):
d14a1e28 560 dispatcher.send(signal='FontDecrease')
486afba9 561
d14a1e28 562 # Default font size.
486afba9 563 elif controlDown and key in (ord('='), wx.WXK_NUMPAD_DIVIDE):
d14a1e28 564 dispatcher.send(signal='FontDefault')
486afba9 565
d14a1e28
RD
566 # Cut to the clipboard.
567 elif (controlDown and key in (ord('X'), ord('x'))) \
486afba9 568 or (shiftDown and key == wx.WXK_DELETE):
d14a1e28 569 self.Cut()
486afba9 570
d14a1e28
RD
571 # Copy to the clipboard.
572 elif controlDown and not shiftDown \
486afba9 573 and key in (ord('C'), ord('c'), wx.WXK_INSERT):
d14a1e28 574 self.Copy()
486afba9 575
d14a1e28
RD
576 # Copy to the clipboard, including prompts.
577 elif controlDown and shiftDown \
486afba9 578 and key in (ord('C'), ord('c'), wx.WXK_INSERT):
d14a1e28 579 self.CopyWithPrompts()
486afba9 580
d14a1e28
RD
581 # Copy to the clipboard, including prefixed prompts.
582 elif altDown and not controlDown \
486afba9 583 and key in (ord('C'), ord('c'), wx.WXK_INSERT):
d14a1e28 584 self.CopyWithPromptsPrefixed()
486afba9 585
d14a1e28
RD
586 # Home needs to be aware of the prompt.
587 elif key == wx.WXK_HOME:
588 home = self.promptPosEnd
589 if currpos > home:
590 self.SetCurrentPos(home)
591 if not selecting and not shiftDown:
592 self.SetAnchor(home)
593 self.EnsureCaretVisible()
594 else:
595 event.Skip()
486afba9 596
d14a1e28
RD
597 #
598 # The following handlers modify text, so we need to see if
599 # there is a selection that includes text prior to the prompt.
600 #
601 # Don't modify a selection with text prior to the prompt.
602 elif selecting and key not in NAVKEYS and not self.CanEdit():
603 pass
486afba9 604
d14a1e28
RD
605 # Paste from the clipboard.
606 elif (controlDown and not shiftDown and key in (ord('V'), ord('v'))) \
607 or (shiftDown and not controlDown and key == wx.WXK_INSERT):
608 self.Paste()
486afba9
RD
609
610 # manually invoke AutoComplete and Calltips
02b800ce 611 elif controlDown and key == wx.WXK_SPACE:
486afba9
RD
612 self.OnCallTipAutoCompleteManually(shiftDown)
613
d14a1e28
RD
614 # Paste from the clipboard, run commands.
615 elif controlDown and shiftDown and key in (ord('V'), ord('v')):
616 self.PasteAndRun()
486afba9 617
d14a1e28
RD
618 # Replace with the previous command from the history buffer.
619 elif (controlDown and key == wx.WXK_UP) \
620 or (altDown and key in (ord('P'), ord('p'))):
621 self.OnHistoryReplace(step=+1)
486afba9 622
d14a1e28
RD
623 # Replace with the next command from the history buffer.
624 elif (controlDown and key == wx.WXK_DOWN) \
625 or (altDown and key in (ord('N'), ord('n'))):
626 self.OnHistoryReplace(step=-1)
486afba9 627
d14a1e28
RD
628 # Insert the previous command from the history buffer.
629 elif (shiftDown and key == wx.WXK_UP) and self.CanEdit():
630 self.OnHistoryInsert(step=+1)
486afba9 631
d14a1e28
RD
632 # Insert the next command from the history buffer.
633 elif (shiftDown and key == wx.WXK_DOWN) and self.CanEdit():
634 self.OnHistoryInsert(step=-1)
486afba9 635
d14a1e28
RD
636 # Search up the history for the text in front of the cursor.
637 elif key == wx.WXK_F8:
638 self.OnHistorySearch()
486afba9 639
d14a1e28
RD
640 # Don't backspace over the latest non-continuation prompt.
641 elif key == wx.WXK_BACK:
642 if selecting and self.CanEdit():
643 event.Skip()
644 elif currpos > self.promptPosEnd:
645 event.Skip()
486afba9 646
d14a1e28
RD
647 # Only allow these keys after the latest prompt.
648 elif key in (wx.WXK_TAB, wx.WXK_DELETE):
649 if self.CanEdit():
650 event.Skip()
486afba9 651
d14a1e28
RD
652 # Don't toggle between insert mode and overwrite mode.
653 elif key == wx.WXK_INSERT:
654 pass
486afba9 655
d14a1e28
RD
656 # Don't allow line deletion.
657 elif controlDown and key in (ord('L'), ord('l')):
658 pass
486afba9 659
d14a1e28
RD
660 # Don't allow line transposition.
661 elif controlDown and key in (ord('T'), ord('t')):
662 pass
486afba9 663
d14a1e28
RD
664 # Basic navigation keys should work anywhere.
665 elif key in NAVKEYS:
666 event.Skip()
486afba9 667
d14a1e28
RD
668 # Protect the readonly portion of the shell.
669 elif not self.CanEdit():
670 pass
486afba9 671
d14a1e28
RD
672 else:
673 event.Skip()
674
486afba9 675
02b800ce
RD
676 def OnShowCompHistory(self):
677 """Show possible autocompletion Words from already typed words."""
678
679 #copy from history
680 his = self.history[:]
681
682 #put together in one string
683 joined = " ".join (his)
684 import re
685
686 #sort out only "good" words
687 newlist = re.split("[ \.\[\]=}(\)\,0-9\"]", joined)
688
689 #length > 1 (mix out "trash")
690 thlist = []
691 for i in newlist:
692 if len (i) > 1:
693 thlist.append (i)
694
695 #unique (no duplicate words
696 #oneliner from german python forum => unique list
697 unlist = [thlist[i] for i in xrange(len(thlist)) if thlist[i] not in thlist[:i]]
698
699 #sort lowercase
700 unlist.sort(lambda a, b: cmp(a.lower(), b.lower()))
701
702 #this is more convenient, isn't it?
703 self.AutoCompSetIgnoreCase(True)
704
705 #join again together in a string
706 stringlist = " ".join(unlist)
707
708 #pos von 0 noch ausrechnen
709
710 #how big is the offset?
711 cpos = self.GetCurrentPos() - 1
712 while chr (self.GetCharAt (cpos)).isalnum():
713 cpos -= 1
714
715 #the most important part
716 self.AutoCompShow(self.GetCurrentPos() - cpos -1, stringlist)
717
718
d14a1e28
RD
719 def clearCommand(self):
720 """Delete the current, unexecuted command."""
721 startpos = self.promptPosEnd
722 endpos = self.GetTextLength()
723 self.SetSelection(startpos, endpos)
724 self.ReplaceSelection('')
725 self.more = False
726
727 def OnHistoryReplace(self, step):
728 """Replace with the previous/next command from the history buffer."""
729 self.clearCommand()
730 self.replaceFromHistory(step)
731
732 def replaceFromHistory(self, step):
733 """Replace selection with command from the history buffer."""
734 ps2 = str(sys.ps2)
735 self.ReplaceSelection('')
736 newindex = self.historyIndex + step
737 if -1 <= newindex <= len(self.history):
738 self.historyIndex = newindex
739 if 0 <= newindex <= len(self.history)-1:
740 command = self.history[self.historyIndex]
741 command = command.replace('\n', os.linesep + ps2)
742 self.ReplaceSelection(command)
743
744 def OnHistoryInsert(self, step):
745 """Insert the previous/next command from the history buffer."""
746 if not self.CanEdit():
747 return
748 startpos = self.GetCurrentPos()
749 self.replaceFromHistory(step)
750 endpos = self.GetCurrentPos()
751 self.SetSelection(endpos, startpos)
752
753 def OnHistorySearch(self):
754 """Search up the history buffer for the text in front of the cursor."""
755 if not self.CanEdit():
756 return
757 startpos = self.GetCurrentPos()
758 # The text up to the cursor is what we search for.
759 numCharsAfterCursor = self.GetTextLength() - startpos
760 searchText = self.getCommand(rstrip=False)
761 if numCharsAfterCursor > 0:
762 searchText = searchText[:-numCharsAfterCursor]
763 if not searchText:
764 return
765 # Search upwards from the current history position and loop
766 # back to the beginning if we don't find anything.
767 if (self.historyIndex <= -1) \
768 or (self.historyIndex >= len(self.history)-2):
769 searchOrder = range(len(self.history))
770 else:
771 searchOrder = range(self.historyIndex+1, len(self.history)) + \
772 range(self.historyIndex)
773 for i in searchOrder:
774 command = self.history[i]
775 if command[:len(searchText)] == searchText:
776 # Replace the current selection with the one we found.
777 self.ReplaceSelection(command[len(searchText):])
778 endpos = self.GetCurrentPos()
779 self.SetSelection(endpos, startpos)
780 # We've now warped into middle of the history.
781 self.historyIndex = i
782 break
783
784 def setStatusText(self, text):
785 """Display status information."""
786
787 # This method will likely be replaced by the enclosing app to
788 # do something more interesting, like write to a status bar.
789 print text
790
791 def insertLineBreak(self):
792 """Insert a new line break."""
793 if self.CanEdit():
794 self.write(os.linesep)
795 self.more = True
796 self.prompt()
797
798 def processLine(self):
799 """Process the line of text at which the user hit Enter."""
800
801 # The user hit ENTER and we need to decide what to do. They
802 # could be sitting on any line in the shell.
803
804 thepos = self.GetCurrentPos()
805 startpos = self.promptPosEnd
806 endpos = self.GetTextLength()
807 ps2 = str(sys.ps2)
808 # If they hit RETURN inside the current command, execute the
809 # command.
810 if self.CanEdit():
811 self.SetCurrentPos(endpos)
812 self.interp.more = False
813 command = self.GetTextRange(startpos, endpos)
814 lines = command.split(os.linesep + ps2)
815 lines = [line.rstrip() for line in lines]
816 command = '\n'.join(lines)
817 if self.reader.isreading:
818 if not command:
819 # Match the behavior of the standard Python shell
820 # when the user hits return without entering a
821 # value.
822 command = '\n'
823 self.reader.input = command
824 self.write(os.linesep)
825 else:
826 self.push(command)
02b800ce 827 wx.FutureCall(1, self.EnsureCaretVisible)
d14a1e28
RD
828 # Or replace the current command with the other command.
829 else:
830 # If the line contains a command (even an invalid one).
831 if self.getCommand(rstrip=False):
832 command = self.getMultilineCommand()
833 self.clearCommand()
834 self.write(command)
835 # Otherwise, put the cursor back where we started.
836 else:
837 self.SetCurrentPos(thepos)
838 self.SetAnchor(thepos)
839
840 def getMultilineCommand(self, rstrip=True):
841 """Extract a multi-line command from the editor.
842
843 The command may not necessarily be valid Python syntax."""
844 # XXX Need to extract real prompts here. Need to keep track of
845 # the prompt every time a command is issued.
846 ps1 = str(sys.ps1)
847 ps1size = len(ps1)
848 ps2 = str(sys.ps2)
849 ps2size = len(ps2)
850 # This is a total hack job, but it works.
851 text = self.GetCurLine()[0]
852 line = self.GetCurrentLine()
853 while text[:ps2size] == ps2 and line > 0:
854 line -= 1
855 self.GotoLine(line)
856 text = self.GetCurLine()[0]
857 if text[:ps1size] == ps1:
858 line = self.GetCurrentLine()
859 self.GotoLine(line)
860 startpos = self.GetCurrentPos() + ps1size
861 line += 1
862 self.GotoLine(line)
863 while self.GetCurLine()[0][:ps2size] == ps2:
864 line += 1
865 self.GotoLine(line)
866 stoppos = self.GetCurrentPos()
867 command = self.GetTextRange(startpos, stoppos)
868 command = command.replace(os.linesep + ps2, '\n')
869 command = command.rstrip()
870 command = command.replace('\n', os.linesep + ps2)
871 else:
872 command = ''
873 if rstrip:
874 command = command.rstrip()
875 return command
876
877 def getCommand(self, text=None, rstrip=True):
878 """Extract a command from text which may include a shell prompt.
879
880 The command may not necessarily be valid Python syntax."""
881 if not text:
882 text = self.GetCurLine()[0]
883 # Strip the prompt off the front leaving just the command.
884 command = self.lstripPrompt(text)
885 if command == text:
886 command = '' # Real commands have prompts.
887 if rstrip:
888 command = command.rstrip()
889 return command
890
891 def lstripPrompt(self, text):
892 """Return text without a leading prompt."""
893 ps1 = str(sys.ps1)
894 ps1size = len(ps1)
895 ps2 = str(sys.ps2)
896 ps2size = len(ps2)
897 # Strip the prompt off the front of text.
898 if text[:ps1size] == ps1:
899 text = text[ps1size:]
900 elif text[:ps2size] == ps2:
901 text = text[ps2size:]
902 return text
903
02b800ce 904 def push(self, command, silent = False):
d14a1e28 905 """Send command to the interpreter for execution."""
02b800ce
RD
906 if not silent:
907 self.write(os.linesep)
d14a1e28
RD
908 busy = wx.BusyCursor()
909 self.waiting = True
910 self.more = self.interp.push(command)
911 self.waiting = False
912 del busy
913 if not self.more:
914 self.addHistory(command.rstrip())
02b800ce
RD
915 if not silent:
916 self.prompt()
d14a1e28
RD
917
918 def addHistory(self, command):
919 """Add command to the command history."""
920 # Reset the history position.
921 self.historyIndex = -1
922 # Insert this command into the history, unless it's a blank
923 # line or the same as the last command.
924 if command != '' \
925 and (len(self.history) == 0 or command != self.history[0]):
926 self.history.insert(0, command)
e773f79b 927 dispatcher.send(signal="Shell.addHistory", command=command)
d14a1e28
RD
928
929 def write(self, text):
930 """Display text in the shell.
931
932 Replace line endings with OS-specific endings."""
933 text = self.fixLineEndings(text)
934 self.AddText(text)
935 self.EnsureCaretVisible()
936
937 def fixLineEndings(self, text):
938 """Return text with line endings replaced by OS-specific endings."""
939 lines = text.split('\r\n')
940 for l in range(len(lines)):
941 chunks = lines[l].split('\r')
942 for c in range(len(chunks)):
943 chunks[c] = os.linesep.join(chunks[c].split('\n'))
944 lines[l] = os.linesep.join(chunks)
945 text = os.linesep.join(lines)
946 return text
947
948 def prompt(self):
949 """Display proper prompt for the context: ps1, ps2 or ps3.
950
951 If this is a continuation line, autoindent as necessary."""
952 isreading = self.reader.isreading
953 skip = False
954 if isreading:
955 prompt = str(sys.ps3)
956 elif self.more:
957 prompt = str(sys.ps2)
958 else:
959 prompt = str(sys.ps1)
960 pos = self.GetCurLine()[1]
961 if pos > 0:
962 if isreading:
963 skip = True
964 else:
965 self.write(os.linesep)
966 if not self.more:
967 self.promptPosStart = self.GetCurrentPos()
968 if not skip:
969 self.write(prompt)
970 if not self.more:
971 self.promptPosEnd = self.GetCurrentPos()
972 # Keep the undo feature from undoing previous responses.
973 self.EmptyUndoBuffer()
974 # XXX Add some autoindent magic here if more.
975 if self.more:
976 self.write(' '*4) # Temporary hack indentation.
977 self.EnsureCaretVisible()
978 self.ScrollToColumn(0)
979
980 def readline(self):
981 """Replacement for stdin.readline()."""
982 input = ''
983 reader = self.reader
984 reader.isreading = True
985 self.prompt()
986 try:
987 while not reader.input:
988 wx.YieldIfNeeded()
989 input = reader.input
990 finally:
991 reader.input = ''
992 reader.isreading = False
993 input = str(input) # In case of Unicode.
994 return input
995
996 def readlines(self):
997 """Replacement for stdin.readlines()."""
998 lines = []
999 while lines[-1:] != ['\n']:
1000 lines.append(self.readline())
1001 return lines
1002
1003 def raw_input(self, prompt=''):
1004 """Return string based on user input."""
1005 if prompt:
1006 self.write(prompt)
1007 return self.readline()
1008
1009 def ask(self, prompt='Please enter your response:'):
1010 """Get response from the user using a dialog box."""
1011 dialog = wx.TextEntryDialog(None, prompt,
1012 'Input Dialog (Raw)', '')
1013 try:
1014 if dialog.ShowModal() == wx.ID_OK:
1015 text = dialog.GetValue()
1016 return text
1017 finally:
1018 dialog.Destroy()
1019 return ''
1020
1021 def pause(self):
1022 """Halt execution pending a response from the user."""
1023 self.ask('Press enter to continue:')
1024
1025 def clear(self):
1026 """Delete all text from the shell."""
1027 self.ClearAll()
1028
1029 def run(self, command, prompt=True, verbose=True):
1030 """Execute command as if it was typed in directly.
1031 >>> shell.run('print "this"')
1032 >>> print "this"
1033 this
1034 >>>
1035 """
1036 # Go to the very bottom of the text.
1037 endpos = self.GetTextLength()
1038 self.SetCurrentPos(endpos)
1039 command = command.rstrip()
1040 if prompt: self.prompt()
1041 if verbose: self.write(command)
1042 self.push(command)
1043
1044 def runfile(self, filename):
1045 """Execute all commands in file as if they were typed into the
1046 shell."""
1047 file = open(filename)
1048 try:
1049 self.prompt()
1050 for command in file.readlines():
1051 if command[:6] == 'shell.':
1052 # Run shell methods silently.
1053 self.run(command, prompt=False, verbose=False)
1054 else:
1055 self.run(command, prompt=False, verbose=True)
1056 finally:
1057 file.close()
1058
02b800ce 1059 def autoCompleteShow(self, command, offset = 0):
d14a1e28 1060 """Display auto-completion popup list."""
d351525a
PB
1061 self.AutoCompSetAutoHide(self.autoCompleteAutoHide)
1062 self.AutoCompSetIgnoreCase(self.autoCompleteCaseInsensitive)
d14a1e28
RD
1063 list = self.interp.getAutoCompleteList(command,
1064 includeMagic=self.autoCompleteIncludeMagic,
1065 includeSingle=self.autoCompleteIncludeSingle,
1066 includeDouble=self.autoCompleteIncludeDouble)
1067 if list:
1068 options = ' '.join(list)
02b800ce 1069 #offset = 0
d14a1e28
RD
1070 self.AutoCompShow(offset, options)
1071
02b800ce 1072 def autoCallTipShow(self, command, insertcalltip = True, forceCallTip = False):
d14a1e28
RD
1073 """Display argument spec and docstring in a popup window."""
1074 if self.CallTipActive():
1075 self.CallTipCancel()
1076 (name, argspec, tip) = self.interp.getCallTip(command)
1077 if tip:
1078 dispatcher.send(signal='Shell.calltip', sender=self, calltip=tip)
02b800ce 1079 if not self.autoCallTip and not forceCallTip:
d14a1e28 1080 return
02b800ce 1081 if argspec and insertcalltip and self.callTipInsert:
d14a1e28
RD
1082 startpos = self.GetCurrentPos()
1083 self.write(argspec + ')')
1084 endpos = self.GetCurrentPos()
1085 self.SetSelection(endpos, startpos)
1086 if tip:
1087 curpos = self.GetCurrentPos()
1088 tippos = curpos - (len(name) + 1)
1089 fallback = curpos - self.GetColumn(curpos)
1090 # In case there isn't enough room, only go back to the
1091 # fallback.
1092 tippos = max(tippos, fallback)
1093 self.CallTipShow(tippos, tip)
02b800ce
RD
1094
1095 def OnCallTipAutoCompleteManually (self, shiftDown):
1096 """AutoComplete and Calltips manually."""
1097 if self.AutoCompActive():
1098 self.AutoCompCancel()
1099 currpos = self.GetCurrentPos()
1100 stoppos = self.promptPosEnd
1101
1102 cpos = currpos
1103 #go back until '.' is found
1104 pointavailpos = -1
1105 while cpos >= stoppos:
1106 if self.GetCharAt(cpos) == ord ('.'):
1107 pointavailpos = cpos
1108 break
1109 cpos -= 1
1110
1111 #word from non whitespace until '.'
1112 if pointavailpos != -1:
1113 #look backward for first whitespace char
1114 textbehind = self.GetTextRange (pointavailpos + 1, currpos)
1115 pointavailpos += 1
1116
1117 if not shiftDown:
1118 #call AutoComplete
1119 stoppos = self.promptPosEnd
1120 textbefore = self.GetTextRange(stoppos, pointavailpos)
1121 self.autoCompleteShow(textbefore, len (textbehind))
1122 else:
1123 #call CallTips
1124 cpos = pointavailpos
1125 begpos = -1
1126 while cpos > stoppos:
1127 if chr(self.GetCharAt(cpos)).isspace():
1128 begpos = cpos
1129 break
1130 cpos -= 1
1131 if begpos == -1:
1132 begpos = cpos
1133 ctips = self.GetTextRange (begpos, currpos)
1134 ctindex = ctips.find ('(')
1135 if ctindex != -1 and not self.CallTipActive():
1136 #insert calltip, if current pos is '(', otherwise show it only
095315e2
RD
1137 self.autoCallTipShow(ctips[:ctindex + 1],
1138 self.GetCharAt(currpos - 1) == ord('(') and self.GetCurrentPos() == self.GetTextLength(),
02b800ce
RD
1139 True)
1140
d14a1e28
RD
1141
1142 def writeOut(self, text):
1143 """Replacement for stdout."""
1144 self.write(text)
1145
1146 def writeErr(self, text):
1147 """Replacement for stderr."""
1148 self.write(text)
1149
1150 def redirectStdin(self, redirect=True):
1151 """If redirect is true then sys.stdin will come from the shell."""
1152 if redirect:
1153 sys.stdin = self.reader
1154 else:
1155 sys.stdin = self.stdin
1156
1157 def redirectStdout(self, redirect=True):
1158 """If redirect is true then sys.stdout will go to the shell."""
1159 if redirect:
1160 sys.stdout = PseudoFileOut(self.writeOut)
1161 else:
1162 sys.stdout = self.stdout
1163
1164 def redirectStderr(self, redirect=True):
1165 """If redirect is true then sys.stderr will go to the shell."""
1166 if redirect:
1167 sys.stderr = PseudoFileErr(self.writeErr)
1168 else:
1169 sys.stderr = self.stderr
1170
1171 def CanCut(self):
1172 """Return true if text is selected and can be cut."""
1173 if self.GetSelectionStart() != self.GetSelectionEnd() \
1174 and self.GetSelectionStart() >= self.promptPosEnd \
1175 and self.GetSelectionEnd() >= self.promptPosEnd:
1176 return True
1177 else:
1178 return False
1179
1180 def CanPaste(self):
1181 """Return true if a paste should succeed."""
1182 if self.CanEdit() and editwindow.EditWindow.CanPaste(self):
1183 return True
1184 else:
1185 return False
1186
1187 def CanEdit(self):
1188 """Return true if editing should succeed."""
1189 if self.GetSelectionStart() != self.GetSelectionEnd():
1190 if self.GetSelectionStart() >= self.promptPosEnd \
1191 and self.GetSelectionEnd() >= self.promptPosEnd:
1192 return True
1193 else:
1194 return False
1195 else:
1196 return self.GetCurrentPos() >= self.promptPosEnd
1197
1198 def Cut(self):
1199 """Remove selection and place it on the clipboard."""
1200 if self.CanCut() and self.CanCopy():
1201 if self.AutoCompActive():
1202 self.AutoCompCancel()
1203 if self.CallTipActive():
1204 self.CallTipCancel()
1205 self.Copy()
1206 self.ReplaceSelection('')
1207
1208 def Copy(self):
1209 """Copy selection and place it on the clipboard."""
1210 if self.CanCopy():
1211 ps1 = str(sys.ps1)
1212 ps2 = str(sys.ps2)
1213 command = self.GetSelectedText()
1214 command = command.replace(os.linesep + ps2, os.linesep)
1215 command = command.replace(os.linesep + ps1, os.linesep)
1216 command = self.lstripPrompt(text=command)
1217 data = wx.TextDataObject(command)
1218 self._clip(data)
1219
1220 def CopyWithPrompts(self):
1221 """Copy selection, including prompts, and place it on the clipboard."""
1222 if self.CanCopy():
1223 command = self.GetSelectedText()
1224 data = wx.TextDataObject(command)
1225 self._clip(data)
1226
1227 def CopyWithPromptsPrefixed(self):
1228 """Copy selection, including prompts prefixed with four
1229 spaces, and place it on the clipboard."""
1230 if self.CanCopy():
1231 command = self.GetSelectedText()
1232 spaces = ' ' * 4
1233 command = spaces + command.replace(os.linesep,
1234 os.linesep + spaces)
1235 data = wx.TextDataObject(command)
1236 self._clip(data)
1237
1238 def _clip(self, data):
1239 if wx.TheClipboard.Open():
1240 wx.TheClipboard.UsePrimarySelection(False)
1241 wx.TheClipboard.SetData(data)
1242 wx.TheClipboard.Flush()
1243 wx.TheClipboard.Close()
1244
1245 def Paste(self):
1246 """Replace selection with clipboard contents."""
1247 if self.CanPaste() and wx.TheClipboard.Open():
1248 ps2 = str(sys.ps2)
1249 if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)):
1250 data = wx.TextDataObject()
1251 if wx.TheClipboard.GetData(data):
1252 self.ReplaceSelection('')
1253 command = data.GetText()
1254 command = command.rstrip()
1255 command = self.fixLineEndings(command)
1256 command = self.lstripPrompt(text=command)
1257 command = command.replace(os.linesep + ps2, '\n')
1258 command = command.replace(os.linesep, '\n')
1259 command = command.replace('\n', os.linesep + ps2)
1260 self.write(command)
1261 wx.TheClipboard.Close()
1262
095315e2 1263
d14a1e28
RD
1264 def PasteAndRun(self):
1265 """Replace selection with clipboard contents, run commands."""
095315e2 1266 text = ''
d14a1e28 1267 if wx.TheClipboard.Open():
d14a1e28
RD
1268 if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)):
1269 data = wx.TextDataObject()
1270 if wx.TheClipboard.GetData(data):
d14a1e28 1271 text = data.GetText()
d14a1e28 1272 wx.TheClipboard.Close()
095315e2
RD
1273 if text:
1274 self.Execute(text)
1275
1276
1277 def Execute(self, text):
1278 """Replace selection with text and run commands."""
1279 ps1 = str(sys.ps1)
1280 ps2 = str(sys.ps2)
1281 endpos = self.GetTextLength()
1282 self.SetCurrentPos(endpos)
1283 startpos = self.promptPosEnd
1284 self.SetSelection(startpos, endpos)
1285 self.ReplaceSelection('')
1286 text = text.lstrip()
1287 text = self.fixLineEndings(text)
1288 text = self.lstripPrompt(text)
1289 text = text.replace(os.linesep + ps1, '\n')
1290 text = text.replace(os.linesep + ps2, '\n')
1291 text = text.replace(os.linesep, '\n')
1292 lines = text.split('\n')
1293 commands = []
1294 command = ''
1295 for line in lines:
1296 if line.strip() == ps2.strip():
1297 # If we are pasting from something like a
1298 # web page that drops the trailing space
1299 # from the ps2 prompt of a blank line.
1300 line = ''
1301 lstrip = line.lstrip()
1302 if line.strip() != '' and lstrip == line and \
1303 lstrip[:4] not in ['else','elif'] and \
1304 lstrip[:6] != 'except':
1305 # New command.
1306 if command:
1307 # Add the previous command to the list.
1308 commands.append(command)
1309 # Start a new command, which may be multiline.
1310 command = line
1311 else:
1312 # Multiline command. Add to the command.
1313 command += '\n'
1314 command += line
1315 commands.append(command)
1316 for command in commands:
1317 command = command.replace('\n', os.linesep + ps2)
1318 self.write(command)
1319 self.processLine()
1320
d14a1e28
RD
1321
1322 def wrap(self, wrap=True):
1323 """Sets whether text is word wrapped."""
1324 try:
1325 self.SetWrapMode(wrap)
1326 except AttributeError:
1327 return 'Wrapping is not available in this version.'
1328
1329 def zoom(self, points=0):
1330 """Set the zoom level.
1331
1332 This number of points is added to the size of all fonts. It
1333 may be positive to magnify or negative to reduce."""
1334 self.SetZoom(points)
02b800ce
RD
1335
1336
1337
1338 def LoadSettings(self, config):
1339 self.autoComplete = config.ReadBool('Options/AutoComplete', True)
1340 self.autoCompleteIncludeMagic = config.ReadBool('Options/AutoCompleteIncludeMagic', True)
1341 self.autoCompleteIncludeSingle = config.ReadBool('Options/AutoCompleteIncludeSingle', True)
1342 self.autoCompleteIncludeDouble = config.ReadBool('Options/AutoCompleteIncludeDouble', True)
1343
1344 self.autoCallTip = config.ReadBool('Options/AutoCallTip', True)
1345 self.callTipInsert = config.ReadBool('Options/CallTipInsert', True)
1346 self.SetWrapMode(config.ReadBool('View/WrapMode', True))
1347
1348 useAA = config.ReadBool('Options/UseAntiAliasing', self.GetUseAntiAliasing())
1349 self.SetUseAntiAliasing(useAA)
1350 self.lineNumbers = config.ReadBool('View/ShowLineNumbers', True)
1351 self.setDisplayLineNumbers (self.lineNumbers)
1352 zoom = config.ReadInt('View/Zoom/Shell', -99)
1353 if zoom != -99:
1354 self.SetZoom(zoom)
1355
1356
1357
1358 def SaveSettings(self, config):
1359 config.WriteBool('Options/AutoComplete', self.autoComplete)
1360 config.WriteBool('Options/AutoCompleteIncludeMagic', self.autoCompleteIncludeMagic)
1361 config.WriteBool('Options/AutoCompleteIncludeSingle', self.autoCompleteIncludeSingle)
1362 config.WriteBool('Options/AutoCompleteIncludeDouble', self.autoCompleteIncludeDouble)
1363 config.WriteBool('Options/AutoCallTip', self.autoCallTip)
1364 config.WriteBool('Options/CallTipInsert', self.callTipInsert)
1365 config.WriteBool('Options/UseAntiAliasing', self.GetUseAntiAliasing())
1366 config.WriteBool('View/WrapMode', self.GetWrapMode())
1367 config.WriteBool('View/ShowLineNumbers', self.lineNumbers)
1368 config.WriteInt('View/Zoom/Shell', self.GetZoom())
1369
1370
1371
1372## NOTE: The DnD of file names is disabled until I can figure out how
1373## best to still allow DnD of text.
1374
1375
1376## #seb : File drag and drop
1377## class FileDropTarget(wx.FileDropTarget):
1378## def __init__(self, obj):
1379## wx.FileDropTarget.__init__(self)
1380## self.obj = obj
1381## def OnDropFiles(self, x, y, filenames):
1382## if len(filenames) == 1:
1383## txt = 'r\"%s\"' % filenames[0]
1384## else:
1385## txt = '( '
1386## for f in filenames:
1387## txt += 'r\"%s\" , ' % f
1388## txt += ')'
1389## self.obj.AppendText(txt)
1390## pos = self.obj.GetCurrentPos()
1391## self.obj.SetCurrentPos( pos )
1392## self.obj.SetSelection( pos, pos )
1393
1394
1395
1396## class TextAndFileDropTarget(wx.DropTarget):
1397## def __init__(self, shell):
1398## wx.DropTarget.__init__(self)
1399## self.shell = shell
1400## self.compdo = wx.DataObjectComposite()
1401## self.textdo = wx.TextDataObject()
1402## self.filedo = wx.FileDataObject()
1403## self.compdo.Add(self.textdo)
1404## self.compdo.Add(self.filedo, True)
1405
1406## self.SetDataObject(self.compdo)
1407
1408## def OnDrop(self, x, y):
1409## return True
1410
1411## def OnData(self, x, y, result):
1412## self.GetData()
1413## if self.textdo.GetTextLength() > 1:
1414## text = self.textdo.GetText()
1415## # *** Do somethign with the dragged text here...
1416## self.textdo.SetText('')
1417## else:
1418## filenames = str(self.filename.GetFilenames())
1419## if len(filenames) == 1:
1420## txt = 'r\"%s\"' % filenames[0]
1421## else:
1422## txt = '( '
1423## for f in filenames:
1424## txt += 'r\"%s\" , ' % f
1425## txt += ')'
1426## self.shell.AppendText(txt)
1427## pos = self.shell.GetCurrentPos()
1428## self.shell.SetCurrentPos( pos )
1429## self.shell.SetSelection( pos, pos )
1430
1431## return result
1432