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