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