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