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