]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/wx/py/shell.py
little tweaks
[wxWidgets.git] / wxPython / wx / py / shell.py
index 8e1b0474f3cb05f6806cf6964a007ae6ef14b0b3..c86ced152d88d50d8e0d5498df966302e1c2aa18 100644 (file)
@@ -31,7 +31,7 @@ NAVKEYS = (wx.WXK_END, wx.WXK_LEFT, wx.WXK_RIGHT,
            wx.WXK_UP, wx.WXK_DOWN, wx.WXK_PRIOR, wx.WXK_NEXT)
 
 
            wx.WXK_UP, wx.WXK_DOWN, wx.WXK_PRIOR, wx.WXK_NEXT)
 
 
-class ShellFrame(frame.Frame):
+class ShellFrame(frame.Frame, frame.ShellFrameMixin):
     """Frame containing the shell component."""
 
     name = 'Shell Frame'
     """Frame containing the shell component."""
 
     name = 'Shell Frame'
@@ -40,19 +40,31 @@ class ShellFrame(frame.Frame):
     def __init__(self, parent=None, id=-1, title='PyShell',
                  pos=wx.DefaultPosition, size=wx.DefaultSize,
                  style=wx.DEFAULT_FRAME_STYLE, locals=None,
     def __init__(self, parent=None, id=-1, title='PyShell',
                  pos=wx.DefaultPosition, size=wx.DefaultSize,
                  style=wx.DEFAULT_FRAME_STYLE, locals=None,
-                 InterpClass=None, *args, **kwds):
+                 InterpClass=None,
+                 config=None, dataDir=None,
+                 *args, **kwds):
         """Create ShellFrame instance."""
         frame.Frame.__init__(self, parent, id, title, pos, size, style)
         """Create ShellFrame instance."""
         frame.Frame.__init__(self, parent, id, title, pos, size, style)
+        frame.ShellFrameMixin.__init__(self, config, dataDir)
+
+        if size == wx.DefaultSize:
+            self.SetSize((750, 525))
+
         intro = 'PyShell %s - The Flakiest Python Shell' % VERSION
         intro = 'PyShell %s - The Flakiest Python Shell' % VERSION
-        intro += '\nSponsored by Orbtech - ' + \
-                 'Your source for Python programming expertise.'
         self.SetStatusText(intro.replace('\n', ', '))
         self.shell = Shell(parent=self, id=-1, introText=intro,
                            locals=locals, InterpClass=InterpClass,
         self.SetStatusText(intro.replace('\n', ', '))
         self.shell = Shell(parent=self, id=-1, introText=intro,
                            locals=locals, InterpClass=InterpClass,
+                           startupScript=self.startupScript,
+                           execStartupScript=self.execStartupScript,
                            *args, **kwds)
                            *args, **kwds)
+
         # Override the shell so that status messages go to the status bar.
         self.shell.setStatusText = self.SetStatusText
 
         # Override the shell so that status messages go to the status bar.
         self.shell.setStatusText = self.SetStatusText
 
+        self.shell.SetFocus()
+        self.LoadSettings()
+
+
     def OnClose(self, event):
         """Event handler for closing."""
         # This isn't working the way I want, but I'll leave it for now.
     def OnClose(self, event):
         """Event handler for closing."""
         # This isn't working the way I want, but I'll leave it for now.
@@ -60,6 +72,7 @@ class ShellFrame(frame.Frame):
             if event.CanVeto():
                 event.Veto(True)
         else:
             if event.CanVeto():
                 event.Veto(True)
         else:
+            self.SaveSettings()
             self.shell.destroy()
             self.Destroy()
 
             self.shell.destroy()
             self.Destroy()
 
@@ -82,21 +95,33 @@ class ShellFrame(frame.Frame):
         dialog.Destroy()
 
 
         dialog.Destroy()
 
 
-class ShellFacade:
-    """Simplified interface to all shell-related functionality.
+    def OnHelp(self, event):
+        """Show a help dialog."""
+        frame.ShellFrameMixin.OnHelp(self, event)
 
 
-    This is a semi-transparent facade, in that all attributes of other
-    are accessible, even though only some are visible to the user."""
 
 
-    name = 'Shell Interface'
-    revision = __revision__
+    def LoadSettings(self):
+        if self.config is not None:
+            frame.ShellFrameMixin.LoadSettings(self)
+            frame.Frame.LoadSettings(self, self.config)
+            self.shell.LoadSettings(self.config)
 
 
-    def __init__(self, other):
-        """Create a ShellFacade instance."""
-        d = self.__dict__
-        d['other'] = other
-        d['helpText'] = \
-"""
+    def SaveSettings(self, force=False):
+        if self.config is not None:
+            frame.ShellFrameMixin.SaveSettings(self)
+            if self.autoSaveSettings or force:
+                frame.Frame.SaveSettings(self, self.config)
+                self.shell.SaveSettings(self.config)
+
+    def DoSaveSettings(self):
+        if self.config is not None:
+            self.SaveSettings(force=True)
+            self.config.Flush()
+        
+
+
+
+HELP_TEXT = """\
 * Key bindings:
 Home              Go to the beginning of the command or line.
 Shift+Home        Select to the beginning of the command or line.
 * Key bindings:
 Home              Go to the beginning of the command or line.
 Shift+Home        Select to the beginning of the command or line.
@@ -104,6 +129,7 @@ Shift+End         Select to the end of the line.
 End               Go to the end of the line.
 Ctrl+C            Copy selected text, removing prompts.
 Ctrl+Shift+C      Copy selected text, retaining prompts.
 End               Go to the end of the line.
 Ctrl+C            Copy selected text, removing prompts.
 Ctrl+Shift+C      Copy selected text, retaining prompts.
+Alt+C             Copy to the clipboard, including prefixed prompts.
 Ctrl+X            Cut selected text.
 Ctrl+V            Paste from clipboard.
 Ctrl+Shift+V      Paste and run multiple commands from clipboard.
 Ctrl+X            Cut selected text.
 Ctrl+V            Paste from clipboard.
 Ctrl+Shift+V      Paste and run multiple commands from clipboard.
@@ -119,8 +145,31 @@ Ctrl+Enter        Insert new line into multiline command.
 Ctrl+]            Increase font size.
 Ctrl+[            Decrease font size.
 Ctrl+=            Default font size.
 Ctrl+]            Increase font size.
 Ctrl+[            Decrease font size.
 Ctrl+=            Default font size.
+Ctrl-Space        Show Auto Completion.
+Ctrl-Alt-Space    Show Call Tip.
+Shift+Enter       Complete Text from History.
+Ctrl+F            Search 
+F3                Search next
+Ctrl+H            "hide" lines containing selection / "unhide"
+F12               on/off "free-edit" mode
 """
 
 """
 
+class ShellFacade:
+    """Simplified interface to all shell-related functionality.
+
+    This is a semi-transparent facade, in that all attributes of other
+    are accessible, even though only some are visible to the user."""
+
+    name = 'Shell Interface'
+    revision = __revision__
+
+    def __init__(self, other):
+        """Create a ShellFacade instance."""
+        d = self.__dict__
+        d['other'] = other
+        d['helpText'] = HELP_TEXT
+        d['this'] = other.this
+
     def help(self):
         """Display some useful information about how to use the shell."""
         self.write(self.helpText)
     def help(self):
         """Display some useful information about how to use the shell."""
         self.write(self.helpText)
@@ -151,6 +200,7 @@ Ctrl+=            Default font size.
             'autoCompleteIncludeDouble',
             'autoCompleteIncludeMagic',
             'autoCompleteIncludeSingle',
             'autoCompleteIncludeDouble',
             'autoCompleteIncludeMagic',
             'autoCompleteIncludeSingle',
+            'callTipInsert',
             'clear',
             'pause',
             'prompt',
             'clear',
             'pause',
             'prompt',
@@ -167,6 +217,7 @@ Ctrl+=            Default font size.
         return list
 
 
         return list
 
 
+
 class Shell(editwindow.EditWindow):
     """Shell based on StyledTextCtrl."""
 
 class Shell(editwindow.EditWindow):
     """Shell based on StyledTextCtrl."""
 
@@ -175,26 +226,32 @@ class Shell(editwindow.EditWindow):
 
     def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
                  size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
 
     def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
                  size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
-                 introText='', locals=None, InterpClass=None, *args, **kwds):
+                 introText='', locals=None, InterpClass=None,
+                 startupScript=None, execStartupScript=True,
+                 *args, **kwds):
         """Create Shell instance."""
         editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
         self.wrap()
         if locals is None:
             import __main__
             locals = __main__.__dict__
         """Create Shell instance."""
         editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
         self.wrap()
         if locals is None:
             import __main__
             locals = __main__.__dict__
+
         # Grab these so they can be restored by self.redirect* methods.
         self.stdin = sys.stdin
         self.stdout = sys.stdout
         self.stderr = sys.stderr
         # Grab these so they can be restored by self.redirect* methods.
         self.stdin = sys.stdin
         self.stdout = sys.stdout
         self.stderr = sys.stderr
+
         # Import a default interpreter class if one isn't provided.
         if InterpClass == None:
             from interpreter import Interpreter
         else:
             Interpreter = InterpClass
         # Import a default interpreter class if one isn't provided.
         if InterpClass == None:
             from interpreter import Interpreter
         else:
             Interpreter = InterpClass
+
         # Create a replacement for stdin.
         self.reader = PseudoFileIn(self.readline, self.readlines)
         self.reader.input = ''
         self.reader.isreading = False
         # Create a replacement for stdin.
         self.reader = PseudoFileIn(self.readline, self.readlines)
         self.reader.input = ''
         self.reader.isreading = False
+
         # Set up the interpreter.
         self.interp = Interpreter(locals=locals,
                                   rawin=self.raw_input,
         # Set up the interpreter.
         self.interp = Interpreter(locals=locals,
                                   rawin=self.raw_input,
@@ -202,15 +259,20 @@ class Shell(editwindow.EditWindow):
                                   stdout=PseudoFileOut(self.writeOut),
                                   stderr=PseudoFileErr(self.writeErr),
                                   *args, **kwds)
                                   stdout=PseudoFileOut(self.writeOut),
                                   stderr=PseudoFileErr(self.writeErr),
                                   *args, **kwds)
+
         # Set up the buffer.
         self.buffer = Buffer()
         # Set up the buffer.
         self.buffer = Buffer()
+
         # Find out for which keycodes the interpreter will autocomplete.
         self.autoCompleteKeys = self.interp.getAutoCompleteKeys()
         # Find out for which keycodes the interpreter will autocomplete.
         self.autoCompleteKeys = self.interp.getAutoCompleteKeys()
+
         # Keep track of the last non-continuation prompt positions.
         self.promptPosStart = 0
         self.promptPosEnd = 0
         # Keep track of the last non-continuation prompt positions.
         self.promptPosStart = 0
         self.promptPosEnd = 0
+
         # Keep track of multi-line commands.
         self.more = False
         # Keep track of multi-line commands.
         self.more = False
+
         # Create the command history.  Commands are added into the
         # front of the list (ie. at index 0) as they are entered.
         # self.historyIndex is the current position in the history; it
         # Create the command history.  Commands are added into the
         # front of the list (ie. at index 0) as they are entered.
         # self.historyIndex is the current position in the history; it
@@ -220,23 +282,51 @@ class Shell(editwindow.EditWindow):
         # command, not in the history.
         self.history = []
         self.historyIndex = -1
         # command, not in the history.
         self.history = []
         self.historyIndex = -1
+
+        #seb add mode for "free edit"
+        self.noteMode = 0
+        self.MarkerDefine(0,stc.STC_MARK_ROUNDRECT)  # marker for hidden
+        self.searchTxt = ""
+
         # Assign handlers for keyboard events.
         # Assign handlers for keyboard events.
-        wx.EVT_CHAR(self, self.OnChar)
-        wx.EVT_KEY_DOWN(self, self.OnKeyDown)
+        self.Bind(wx.EVT_CHAR, self.OnChar)
+        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
+
         # Assign handler for idle time.
         self.waiting = False
         # Assign handler for idle time.
         self.waiting = False
-        wx.EVT_IDLE(self, self.OnIdle)
+        self.Bind(wx.EVT_IDLE, self.OnIdle)
+
         # Display the introductory banner information.
         self.showIntro(introText)
         # Display the introductory banner information.
         self.showIntro(introText)
+
         # Assign some pseudo keywords to the interpreter's namespace.
         self.setBuiltinKeywords()
         # Assign some pseudo keywords to the interpreter's namespace.
         self.setBuiltinKeywords()
+
         # Add 'shell' to the interpreter's local namespace.
         self.setLocalShell()
         # Add 'shell' to the interpreter's local namespace.
         self.setLocalShell()
+
+        ## NOTE:  See note at bottom of this file...
+        ## #seb: File drag and drop
+        ## self.SetDropTarget( FileDropTarget(self) )
+
         # Do this last so the user has complete control over their
         # environment.  They can override anything they want.
         # Do this last so the user has complete control over their
         # environment.  They can override anything they want.
-        self.execStartupScript(self.interp.startupScript)
+        if execStartupScript:
+            if startupScript is None:
+                startupScript = os.environ.get('PYTHONSTARTUP')
+            self.execStartupScript(startupScript)
+        else:
+            self.prompt()
+        
         wx.CallAfter(self.ScrollToLine, 0)
 
         wx.CallAfter(self.ScrollToLine, 0)
 
+
+    def clearHistory(self):
+        self.history = []
+        self.historyIndex = -1
+        dispatcher.send(signal="Shell.clearHistory")
+
+
     def destroy(self):
         del self.interp
 
     def destroy(self):
         del self.interp
 
@@ -264,35 +354,38 @@ class Shell(editwindow.EditWindow):
     def setBuiltinKeywords(self):
         """Create pseudo keywords as part of builtins.
 
     def setBuiltinKeywords(self):
         """Create pseudo keywords as part of builtins.
 
-        This sets `close`, `exit` and `quit` to a helpful string.
+        This sets "close", "exit" and "quit" to a helpful string.
         """
         import __builtin__
         __builtin__.close = __builtin__.exit = __builtin__.quit = \
             'Click on the close button to leave the application.'
 
         """
         import __builtin__
         __builtin__.close = __builtin__.exit = __builtin__.quit = \
             'Click on the close button to leave the application.'
 
+
     def quit(self):
         """Quit the application."""
     def quit(self):
         """Quit the application."""
-
         # XXX Good enough for now but later we want to send a close event.
         # XXX Good enough for now but later we want to send a close event.
-
         # In the close event handler we can make sure they want to
         # quit.  Other applications, like PythonCard, may choose to
         # hide rather than quit so we should just post the event and
         # let the surrounding app decide what it wants to do.
         self.write('Click on the close button to leave the application.')
 
         # In the close event handler we can make sure they want to
         # quit.  Other applications, like PythonCard, may choose to
         # hide rather than quit so we should just post the event and
         # let the surrounding app decide what it wants to do.
         self.write('Click on the close button to leave the application.')
 
+
     def setLocalShell(self):
         """Add 'shell' to locals as reference to ShellFacade instance."""
         self.interp.locals['shell'] = ShellFacade(other=self)
 
     def setLocalShell(self):
         """Add 'shell' to locals as reference to ShellFacade instance."""
         self.interp.locals['shell'] = ShellFacade(other=self)
 
+
     def execStartupScript(self, startupScript):
         """Execute the user's PYTHONSTARTUP script if they have one."""
         if startupScript and os.path.isfile(startupScript):
             text = 'Startup script executed: ' + startupScript
             self.push('print %r; execfile(%r)' % (text, startupScript))
     def execStartupScript(self, startupScript):
         """Execute the user's PYTHONSTARTUP script if they have one."""
         if startupScript and os.path.isfile(startupScript):
             text = 'Startup script executed: ' + startupScript
             self.push('print %r; execfile(%r)' % (text, startupScript))
+            self.interp.startupScript = startupScript
         else:
             self.push('')
 
         else:
             self.push('')
 
+
     def about(self):
         """Display information about Py."""
         text = """
     def about(self):
         """Display information about Py."""
         text = """
@@ -302,17 +395,24 @@ Py Shell Revision: %s
 Py Interpreter Revision: %s
 Python Version: %s
 wxPython Version: %s
 Py Interpreter Revision: %s
 Python Version: %s
 wxPython Version: %s
+wxPython PlatformInfo: %s
 Platform: %s""" % \
         (__author__, VERSION, self.revision, self.interp.revision,
 Platform: %s""" % \
         (__author__, VERSION, self.revision, self.interp.revision,
-         sys.version.split()[0], wx.VERSION_STRING, sys.platform)
+         sys.version.split()[0], wx.VERSION_STRING, str(wx.PlatformInfo),
+         sys.platform)
         self.write(text.strip())
 
         self.write(text.strip())
 
+
     def OnChar(self, event):
         """Keypress event handler.
 
         Only receives an event if OnKeyDown calls event.Skip() for the
         corresponding event."""
 
     def OnChar(self, event):
         """Keypress event handler.
 
         Only receives an event if OnKeyDown calls event.Skip() for the
         corresponding event."""
 
+        if self.noteMode:
+            event.Skip()
+            return
+
         # Prevent modification of previously submitted
         # commands/responses.
         if not self.CanEdit():
         # Prevent modification of previously submitted
         # commands/responses.
         if not self.CanEdit():
@@ -321,7 +421,7 @@ Platform: %s""" % \
         currpos = self.GetCurrentPos()
         stoppos = self.promptPosEnd
         # Return (Enter) needs to be ignored in this handler.
         currpos = self.GetCurrentPos()
         stoppos = self.promptPosEnd
         # Return (Enter) needs to be ignored in this handler.
-        if key == wx.WXK_RETURN:
+        if key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]:
             pass
         elif key in self.autoCompleteKeys:
             # Usually the dot (period) key activates auto completion.
             pass
         elif key in self.autoCompleteKeys:
             # Usually the dot (period) key activates auto completion.
@@ -343,11 +443,12 @@ Platform: %s""" % \
             self.ReplaceSelection('')
             command = self.GetTextRange(stoppos, currpos) + '('
             self.write('(')
             self.ReplaceSelection('')
             command = self.GetTextRange(stoppos, currpos) + '('
             self.write('(')
-            self.autoCallTipShow(command)
+            self.autoCallTipShow(command, self.GetCurrentPos() == self.GetTextLength())
         else:
             # Allow the normal event handling to take place.
             event.Skip()
 
         else:
             # Allow the normal event handling to take place.
             event.Skip()
 
+
     def OnKeyDown(self, event):
         """Key down event handler."""
 
     def OnKeyDown(self, event):
         """Key down event handler."""
 
@@ -356,6 +457,7 @@ Platform: %s""" % \
         if self.AutoCompActive():
             event.Skip()
             return
         if self.AutoCompActive():
             event.Skip()
             return
+        
         # Prevent modification of previously submitted
         # commands/responses.
         controlDown = event.ControlDown()
         # Prevent modification of previously submitted
         # commands/responses.
         controlDown = event.ControlDown()
@@ -364,54 +466,122 @@ Platform: %s""" % \
         currpos = self.GetCurrentPos()
         endpos = self.GetTextLength()
         selecting = self.GetSelectionStart() != self.GetSelectionEnd()
         currpos = self.GetCurrentPos()
         endpos = self.GetTextLength()
         selecting = self.GetSelectionStart() != self.GetSelectionEnd()
+        
+        if controlDown and shiftDown and key in (ord('F'), ord('f')): 
+            li = self.GetCurrentLine()
+            m = self.MarkerGet(li)
+            if m & 1<<0:
+                startP = self.PositionFromLine(li)
+                self.MarkerDelete(li, 0)
+                maxli = self.GetLineCount()
+                li += 1 # li stayed visible as header-line
+                li0 = li 
+                while li<maxli and self.GetLineVisible(li) == 0:
+                    li += 1
+                endP = self.GetLineEndPosition(li-1)
+                self.ShowLines(li0, li-1)
+                self.SetSelection( startP, endP ) # select reappearing text to allow "hide again"
+                return
+            startP,endP = self.GetSelection()
+            endP-=1
+            startL,endL = self.LineFromPosition(startP), self.LineFromPosition(endP)
+
+            if endL == self.LineFromPosition(self.promptPosEnd): # never hide last prompt
+                endL -= 1
+
+            m = self.MarkerGet(startL)
+            self.MarkerAdd(startL, 0)
+            self.HideLines(startL+1,endL)
+            self.SetCurrentPos( startP ) # to ensure caret stays visible !
+
+        if key == wx.WXK_F12: #seb
+            if self.noteMode:
+                # self.promptPosStart not used anyway - or ? 
+                self.promptPosEnd = self.PositionFromLine( self.GetLineCount()-1 ) + len(str(sys.ps1))
+                self.GotoLine(self.GetLineCount())
+                self.GotoPos(self.promptPosEnd)
+                self.prompt()  #make sure we have a prompt
+                self.SetCaretForeground("black")
+                self.SetCaretWidth(1)    #default
+                self.SetCaretPeriod(500) #default
+            else:
+                self.SetCaretForeground("red")
+                self.SetCaretWidth(4)
+                self.SetCaretPeriod(0) #steady
+
+            self.noteMode = not self.noteMode
+            return
+        if self.noteMode:
+            event.Skip()
+            return
+
         # Return (Enter) is used to submit a command to the
         # interpreter.
         # Return (Enter) is used to submit a command to the
         # interpreter.
-        if not controlDown and key == wx.WXK_RETURN:
+        if (not controlDown and not shiftDown and not altDown) and key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]:
             if self.CallTipActive():
                 self.CallTipCancel()
             self.processLine()
             if self.CallTipActive():
                 self.CallTipCancel()
             self.processLine()
-        # Ctrl+Return (Cntrl+Enter) is used to insert a line break.
-        elif controlDown and key == wx.WXK_RETURN:
+            
+        # Complete Text (from already typed words)    
+        elif shiftDown and key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]:
+            self.OnShowCompHistory()
+            
+        # Ctrl+Return (Ctrl+Enter) is used to insert a line break.
+        elif controlDown and key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]:
             if self.CallTipActive():
                 self.CallTipCancel()
             if currpos == endpos:
                 self.processLine()
             else:
                 self.insertLineBreak()
             if self.CallTipActive():
                 self.CallTipCancel()
             if currpos == endpos:
                 self.processLine()
             else:
                 self.insertLineBreak()
+                
         # Let Ctrl-Alt-* get handled normally.
         elif controlDown and altDown:
             event.Skip()
         # Let Ctrl-Alt-* get handled normally.
         elif controlDown and altDown:
             event.Skip()
+            
         # Clear the current, unexecuted command.
         elif key == wx.WXK_ESCAPE:
             if self.CallTipActive():
                 event.Skip()
             else:
                 self.clearCommand()
         # Clear the current, unexecuted command.
         elif key == wx.WXK_ESCAPE:
             if self.CallTipActive():
                 event.Skip()
             else:
                 self.clearCommand()
+
+        # Clear the current command
+        elif key == wx.WXK_BACK and controlDown and shiftDown:
+            self.clearCommand()
+
         # Increase font size.
         # Increase font size.
-        elif controlDown and key in (ord(']'),):
+        elif controlDown and key in (ord(']'), wx.WXK_NUMPAD_ADD):
             dispatcher.send(signal='FontIncrease')
             dispatcher.send(signal='FontIncrease')
+
         # Decrease font size.
         # Decrease font size.
-        elif controlDown and key in (ord('['),):
+        elif controlDown and key in (ord('['), wx.WXK_NUMPAD_SUBTRACT):
             dispatcher.send(signal='FontDecrease')
             dispatcher.send(signal='FontDecrease')
+
         # Default font size.
         # Default font size.
-        elif controlDown and key in (ord('='),):
+        elif controlDown and key in (ord('='), wx.WXK_NUMPAD_DIVIDE):
             dispatcher.send(signal='FontDefault')
             dispatcher.send(signal='FontDefault')
+
         # Cut to the clipboard.
         elif (controlDown and key in (ord('X'), ord('x'))) \
         # Cut to the clipboard.
         elif (controlDown and key in (ord('X'), ord('x'))) \
-        or (shiftDown and key == wx.WXK_DELETE):
+                 or (shiftDown and key == wx.WXK_DELETE):
             self.Cut()
             self.Cut()
+
         # Copy to the clipboard.
         elif controlDown and not shiftDown \
         # Copy to the clipboard.
         elif controlDown and not shiftDown \
-            and key in (ord('C'), ord('c'), wx.WXK_INSERT):
+                 and key in (ord('C'), ord('c'), wx.WXK_INSERT):
             self.Copy()
             self.Copy()
+
         # Copy to the clipboard, including prompts.
         elif controlDown and shiftDown \
         # Copy to the clipboard, including prompts.
         elif controlDown and shiftDown \
-            and key in (ord('C'), ord('c'), wx.WXK_INSERT):
+                 and key in (ord('C'), ord('c'), wx.WXK_INSERT):
             self.CopyWithPrompts()
             self.CopyWithPrompts()
+
         # Copy to the clipboard, including prefixed prompts.
         elif altDown and not controlDown \
         # Copy to the clipboard, including prefixed prompts.
         elif altDown and not controlDown \
-            and key in (ord('C'), ord('c'), wx.WXK_INSERT):
+                 and key in (ord('C'), ord('c'), wx.WXK_INSERT):
             self.CopyWithPromptsPrefixed()
             self.CopyWithPromptsPrefixed()
+
         # Home needs to be aware of the prompt.
         elif key == wx.WXK_HOME:
             home = self.promptPosEnd
         # Home needs to be aware of the prompt.
         elif key == wx.WXK_HOME:
             home = self.promptPosEnd
@@ -422,6 +592,7 @@ Platform: %s""" % \
                     self.EnsureCaretVisible()
             else:
                 event.Skip()
                     self.EnsureCaretVisible()
             else:
                 event.Skip()
+
         #
         # The following handlers modify text, so we need to see if
         # there is a selection that includes text prior to the prompt.
         #
         # The following handlers modify text, so we need to see if
         # there is a selection that includes text prior to the prompt.
@@ -429,58 +600,121 @@ Platform: %s""" % \
         # Don't modify a selection with text prior to the prompt.
         elif selecting and key not in NAVKEYS and not self.CanEdit():
             pass
         # Don't modify a selection with text prior to the prompt.
         elif selecting and key not in NAVKEYS and not self.CanEdit():
             pass
+
         # Paste from the clipboard.
         elif (controlDown and not shiftDown and key in (ord('V'), ord('v'))) \
                  or (shiftDown and not controlDown and key == wx.WXK_INSERT):
             self.Paste()
         # Paste from the clipboard.
         elif (controlDown and not shiftDown and key in (ord('V'), ord('v'))) \
                  or (shiftDown and not controlDown and key == wx.WXK_INSERT):
             self.Paste()
+
+        # manually invoke AutoComplete and Calltips
+        elif controlDown and key == wx.WXK_SPACE:
+            self.OnCallTipAutoCompleteManually(shiftDown)
+
         # Paste from the clipboard, run commands.
         elif controlDown and shiftDown and key in (ord('V'), ord('v')):
             self.PasteAndRun()
         # Paste from the clipboard, run commands.
         elif controlDown and shiftDown and key in (ord('V'), ord('v')):
             self.PasteAndRun()
+            
         # Replace with the previous command from the history buffer.
         elif (controlDown and key == wx.WXK_UP) \
                  or (altDown and key in (ord('P'), ord('p'))):
             self.OnHistoryReplace(step=+1)
         # Replace with the previous command from the history buffer.
         elif (controlDown and key == wx.WXK_UP) \
                  or (altDown and key in (ord('P'), ord('p'))):
             self.OnHistoryReplace(step=+1)
+            
         # Replace with the next command from the history buffer.
         elif (controlDown and key == wx.WXK_DOWN) \
                  or (altDown and key in (ord('N'), ord('n'))):
             self.OnHistoryReplace(step=-1)
         # Replace with the next command from the history buffer.
         elif (controlDown and key == wx.WXK_DOWN) \
                  or (altDown and key in (ord('N'), ord('n'))):
             self.OnHistoryReplace(step=-1)
+            
         # Insert the previous command from the history buffer.
         elif (shiftDown and key == wx.WXK_UP) and self.CanEdit():
             self.OnHistoryInsert(step=+1)
         # Insert the previous command from the history buffer.
         elif (shiftDown and key == wx.WXK_UP) and self.CanEdit():
             self.OnHistoryInsert(step=+1)
+            
         # Insert the next command from the history buffer.
         elif (shiftDown and key == wx.WXK_DOWN) and self.CanEdit():
             self.OnHistoryInsert(step=-1)
         # Insert the next command from the history buffer.
         elif (shiftDown and key == wx.WXK_DOWN) and self.CanEdit():
             self.OnHistoryInsert(step=-1)
+            
         # Search up the history for the text in front of the cursor.
         elif key == wx.WXK_F8:
             self.OnHistorySearch()
         # Search up the history for the text in front of the cursor.
         elif key == wx.WXK_F8:
             self.OnHistorySearch()
+            
         # Don't backspace over the latest non-continuation prompt.
         elif key == wx.WXK_BACK:
             if selecting and self.CanEdit():
                 event.Skip()
             elif currpos > self.promptPosEnd:
                 event.Skip()
         # Don't backspace over the latest non-continuation prompt.
         elif key == wx.WXK_BACK:
             if selecting and self.CanEdit():
                 event.Skip()
             elif currpos > self.promptPosEnd:
                 event.Skip()
+                
         # Only allow these keys after the latest prompt.
         elif key in (wx.WXK_TAB, wx.WXK_DELETE):
             if self.CanEdit():
                 event.Skip()
         # Only allow these keys after the latest prompt.
         elif key in (wx.WXK_TAB, wx.WXK_DELETE):
             if self.CanEdit():
                 event.Skip()
+                
         # Don't toggle between insert mode and overwrite mode.
         elif key == wx.WXK_INSERT:
             pass
         # Don't toggle between insert mode and overwrite mode.
         elif key == wx.WXK_INSERT:
             pass
+        
         # Don't allow line deletion.
         elif controlDown and key in (ord('L'), ord('l')):
             pass
         # Don't allow line deletion.
         elif controlDown and key in (ord('L'), ord('l')):
             pass
+
         # Don't allow line transposition.
         elif controlDown and key in (ord('T'), ord('t')):
             pass
         # Don't allow line transposition.
         elif controlDown and key in (ord('T'), ord('t')):
             pass
+
         # Basic navigation keys should work anywhere.
         elif key in NAVKEYS:
             event.Skip()
         # Basic navigation keys should work anywhere.
         elif key in NAVKEYS:
             event.Skip()
+
         # Protect the readonly portion of the shell.
         elif not self.CanEdit():
             pass
         # Protect the readonly portion of the shell.
         elif not self.CanEdit():
             pass
+
         else:
             event.Skip()
 
         else:
             event.Skip()
 
+
+    def OnShowCompHistory(self):
+        """Show possible autocompletion Words from already typed words."""
+        
+        #copy from history
+        his = self.history[:]
+        
+        #put together in one string
+        joined = " ".join (his)
+        import re
+
+        #sort out only "good" words
+        newlist = re.split("[ \.\[\]=}(\)\,0-9\"]", joined)
+        
+        #length > 1 (mix out "trash")
+        thlist = []
+        for i in newlist:
+            if len (i) > 1:
+                thlist.append (i)
+        
+        #unique (no duplicate words
+        #oneliner from german python forum => unique list
+        unlist = [thlist[i] for i in xrange(len(thlist)) if thlist[i] not in thlist[:i]]
+            
+        #sort lowercase
+        unlist.sort(lambda a, b: cmp(a.lower(), b.lower()))
+        
+        #this is more convenient, isn't it?
+        self.AutoCompSetIgnoreCase(True)
+        
+        #join again together in a string
+        stringlist = " ".join(unlist)
+
+        #pos von 0 noch ausrechnen
+
+        #how big is the offset?
+        cpos = self.GetCurrentPos() - 1
+        while chr (self.GetCharAt (cpos)).isalnum():
+            cpos -= 1
+            
+        #the most important part
+        self.AutoCompShow(self.GetCurrentPos() - cpos -1, stringlist)
+
+
     def clearCommand(self):
         """Delete the current, unexecuted command."""
         startpos = self.promptPosEnd
     def clearCommand(self):
         """Delete the current, unexecuted command."""
         startpos = self.promptPosEnd
@@ -589,6 +823,7 @@ Platform: %s""" % \
                 self.write(os.linesep)
             else:
                 self.push(command)
                 self.write(os.linesep)
             else:
                 self.push(command)
+                wx.FutureCall(1, self.EnsureCaretVisible)
         # Or replace the current command with the other command.
         else:
             # If the line contains a command (even an invalid one).
         # Or replace the current command with the other command.
         else:
             # If the line contains a command (even an invalid one).
@@ -665,9 +900,10 @@ Platform: %s""" % \
             text = text[ps2size:]
         return text
 
             text = text[ps2size:]
         return text
 
-    def push(self, command):
+    def push(self, command, silent = False):
         """Send command to the interpreter for execution."""
         """Send command to the interpreter for execution."""
-        self.write(os.linesep)
+        if not silent:
+            self.write(os.linesep)
         busy = wx.BusyCursor()
         self.waiting = True
         self.more = self.interp.push(command)
         busy = wx.BusyCursor()
         self.waiting = True
         self.more = self.interp.push(command)
@@ -675,7 +911,8 @@ Platform: %s""" % \
         del busy
         if not self.more:
             self.addHistory(command.rstrip())
         del busy
         if not self.more:
             self.addHistory(command.rstrip())
-        self.prompt()
+        if not silent:
+            self.prompt()
 
     def addHistory(self, command):
         """Add command to the command history."""
 
     def addHistory(self, command):
         """Add command to the command history."""
@@ -686,6 +923,7 @@ Platform: %s""" % \
         if command != '' \
         and (len(self.history) == 0 or command != self.history[0]):
             self.history.insert(0, command)
         if command != '' \
         and (len(self.history) == 0 or command != self.history[0]):
             self.history.insert(0, command)
+            dispatcher.send(signal="Shell.addHistory", command=command)
 
     def write(self, text):
         """Display text in the shell.
 
     def write(self, text):
         """Display text in the shell.
@@ -817,7 +1055,7 @@ Platform: %s""" % \
         finally:
             file.close()
 
         finally:
             file.close()
 
-    def autoCompleteShow(self, command):
+    def autoCompleteShow(self, command, offset = 0):
         """Display auto-completion popup list."""
         self.AutoCompSetAutoHide(self.autoCompleteAutoHide)
         self.AutoCompSetIgnoreCase(self.autoCompleteCaseInsensitive)
         """Display auto-completion popup list."""
         self.AutoCompSetAutoHide(self.autoCompleteAutoHide)
         self.AutoCompSetIgnoreCase(self.autoCompleteCaseInsensitive)
@@ -827,19 +1065,19 @@ Platform: %s""" % \
                     includeDouble=self.autoCompleteIncludeDouble)
         if list:
             options = ' '.join(list)
                     includeDouble=self.autoCompleteIncludeDouble)
         if list:
             options = ' '.join(list)
-            offset = 0
+            #offset = 0
             self.AutoCompShow(offset, options)
 
             self.AutoCompShow(offset, options)
 
-    def autoCallTipShow(self, command):
+    def autoCallTipShow(self, command, insertcalltip = True, forceCallTip = False):
         """Display argument spec and docstring in a popup window."""
         if self.CallTipActive():
             self.CallTipCancel()
         (name, argspec, tip) = self.interp.getCallTip(command)
         if tip:
             dispatcher.send(signal='Shell.calltip', sender=self, calltip=tip)
         """Display argument spec and docstring in a popup window."""
         if self.CallTipActive():
             self.CallTipCancel()
         (name, argspec, tip) = self.interp.getCallTip(command)
         if tip:
             dispatcher.send(signal='Shell.calltip', sender=self, calltip=tip)
-        if not self.autoCallTip:
+        if not self.autoCallTip and not forceCallTip:
             return
             return
-        if argspec:
+        if argspec and insertcalltip and self.callTipInsert:
             startpos = self.GetCurrentPos()
             self.write(argspec + ')')
             endpos = self.GetCurrentPos()
             startpos = self.GetCurrentPos()
             self.write(argspec + ')')
             endpos = self.GetCurrentPos()
@@ -852,6 +1090,53 @@ Platform: %s""" % \
             # fallback.
             tippos = max(tippos, fallback)
             self.CallTipShow(tippos, tip)
             # fallback.
             tippos = max(tippos, fallback)
             self.CallTipShow(tippos, tip)
+    
+    def OnCallTipAutoCompleteManually (self, shiftDown):
+        """AutoComplete and Calltips manually."""
+        if self.AutoCompActive():
+            self.AutoCompCancel()
+        currpos = self.GetCurrentPos()
+        stoppos = self.promptPosEnd
+
+        cpos = currpos
+        #go back until '.' is found
+        pointavailpos = -1
+        while cpos >= stoppos:
+            if self.GetCharAt(cpos) == ord ('.'):
+                pointavailpos = cpos
+                break
+            cpos -= 1
+
+        #word from non whitespace until '.'
+        if pointavailpos != -1:
+            #look backward for first whitespace char
+            textbehind = self.GetTextRange (pointavailpos + 1, currpos)
+            pointavailpos += 1
+
+            if not shiftDown:
+                #call AutoComplete
+                stoppos = self.promptPosEnd
+                textbefore = self.GetTextRange(stoppos, pointavailpos)
+                self.autoCompleteShow(textbefore, len (textbehind))
+            else:
+                #call CallTips
+                cpos = pointavailpos
+                begpos = -1
+                while cpos > stoppos:
+                    if chr(self.GetCharAt(cpos)).isspace():
+                        begpos = cpos
+                        break
+                    cpos -= 1
+                if begpos == -1:
+                    begpos = cpos
+                ctips = self.GetTextRange (begpos, currpos)
+                ctindex = ctips.find ('(')
+                if ctindex != -1 and not self.CallTipActive():
+                    #insert calltip, if current pos is '(', otherwise show it only
+                    self.autoCallTipShow(ctips[:ctindex + 1], 
+                        self.GetCharAt(currpos - 1) == ord('(') and self.GetCurrentPos() == self.GetTextLength(),
+                        True)
+                
 
     def writeOut(self, text):
         """Replacement for stdout."""
 
     def writeOut(self, text):
         """Replacement for stdout."""
@@ -974,52 +1259,64 @@ Platform: %s""" % \
                     self.write(command)
             wx.TheClipboard.Close()
 
                     self.write(command)
             wx.TheClipboard.Close()
 
+
     def PasteAndRun(self):
         """Replace selection with clipboard contents, run commands."""
     def PasteAndRun(self):
         """Replace selection with clipboard contents, run commands."""
+        text = ''
         if wx.TheClipboard.Open():
         if wx.TheClipboard.Open():
-            ps1 = str(sys.ps1)
-            ps2 = str(sys.ps2)
             if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)):
                 data = wx.TextDataObject()
                 if wx.TheClipboard.GetData(data):
             if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)):
                 data = wx.TextDataObject()
                 if wx.TheClipboard.GetData(data):
-                    endpos = self.GetTextLength()
-                    self.SetCurrentPos(endpos)
-                    startpos = self.promptPosEnd
-                    self.SetSelection(startpos, endpos)
-                    self.ReplaceSelection('')
                     text = data.GetText()
                     text = data.GetText()
-                    text = text.lstrip()
-                    text = self.fixLineEndings(text)
-                    text = self.lstripPrompt(text)
-                    text = text.replace(os.linesep + ps1, '\n')
-                    text = text.replace(os.linesep + ps2, '\n')
-                    text = text.replace(os.linesep, '\n')
-                    lines = text.split('\n')
-                    commands = []
-                    command = ''
-                    for line in lines:
-                        if line.strip() == ps2.strip():
-                            # If we are pasting from something like a
-                            # web page that drops the trailing space
-                            # from the ps2 prompt of a blank line.
-                            line = ''
-                        if line.strip() != '' and line.lstrip() == line:
-                            # New command.
-                            if command:
-                                # Add the previous command to the list.
-                                commands.append(command)
-                            # Start a new command, which may be multiline.
-                            command = line
-                        else:
-                            # Multiline command. Add to the command.
-                            command += '\n'
-                            command += line
-                    commands.append(command)
-                    for command in commands:
-                        command = command.replace('\n', os.linesep + ps2)
-                        self.write(command)
-                        self.processLine()
             wx.TheClipboard.Close()
             wx.TheClipboard.Close()
+        if text:
+            self.Execute(text)
+            
+
+    def Execute(self, text):
+        """Replace selection with text and run commands."""
+        ps1 = str(sys.ps1)
+        ps2 = str(sys.ps2)
+        endpos = self.GetTextLength()
+        self.SetCurrentPos(endpos)
+        startpos = self.promptPosEnd
+        self.SetSelection(startpos, endpos)
+        self.ReplaceSelection('')
+        text = text.lstrip()
+        text = self.fixLineEndings(text)
+        text = self.lstripPrompt(text)
+        text = text.replace(os.linesep + ps1, '\n')
+        text = text.replace(os.linesep + ps2, '\n')
+        text = text.replace(os.linesep, '\n')
+        lines = text.split('\n')
+        commands = []
+        command = ''
+        for line in lines:
+            if line.strip() == ps2.strip():
+                # If we are pasting from something like a
+                # web page that drops the trailing space
+                # from the ps2 prompt of a blank line.
+                line = ''
+            lstrip = line.lstrip()
+            if line.strip() != '' and lstrip == line and \
+                    lstrip[:4] not in ['else','elif'] and \
+                    lstrip[:6] != 'except':
+                # New command.
+                if command:
+                    # Add the previous command to the list.
+                    commands.append(command)
+                # Start a new command, which may be multiline.
+                command = line
+            else:
+                # Multiline command. Add to the command.
+                command += '\n'
+                command += line
+        commands.append(command)
+        for command in commands:
+            command = command.replace('\n', os.linesep + ps2)
+            self.write(command)
+            self.processLine()
+
 
     def wrap(self, wrap=True):
         """Sets whether text is word wrapped."""
 
     def wrap(self, wrap=True):
         """Sets whether text is word wrapped."""
@@ -1034,3 +1331,101 @@ Platform: %s""" % \
         This number of points is added to the size of all fonts.  It
         may be positive to magnify or negative to reduce."""
         self.SetZoom(points)
         This number of points is added to the size of all fonts.  It
         may be positive to magnify or negative to reduce."""
         self.SetZoom(points)
+
+
+
+    def LoadSettings(self, config):
+        self.autoComplete              = config.ReadBool('Options/AutoComplete', True)
+        self.autoCompleteIncludeMagic  = config.ReadBool('Options/AutoCompleteIncludeMagic', True)
+        self.autoCompleteIncludeSingle = config.ReadBool('Options/AutoCompleteIncludeSingle', True)
+        self.autoCompleteIncludeDouble = config.ReadBool('Options/AutoCompleteIncludeDouble', True)
+
+        self.autoCallTip = config.ReadBool('Options/AutoCallTip', True)
+        self.callTipInsert = config.ReadBool('Options/CallTipInsert', True)
+        self.SetWrapMode(config.ReadBool('View/WrapMode', True))
+
+        useAA = config.ReadBool('Options/UseAntiAliasing', self.GetUseAntiAliasing())
+        self.SetUseAntiAliasing(useAA)
+        self.lineNumbers = config.ReadBool('View/ShowLineNumbers', True)
+        self.setDisplayLineNumbers (self.lineNumbers)
+        zoom = config.ReadInt('View/Zoom/Shell', -99)
+        if zoom != -99:
+            self.SetZoom(zoom)
+
+
+    
+    def SaveSettings(self, config):
+        config.WriteBool('Options/AutoComplete', self.autoComplete)
+        config.WriteBool('Options/AutoCompleteIncludeMagic', self.autoCompleteIncludeMagic)
+        config.WriteBool('Options/AutoCompleteIncludeSingle', self.autoCompleteIncludeSingle)
+        config.WriteBool('Options/AutoCompleteIncludeDouble', self.autoCompleteIncludeDouble)
+        config.WriteBool('Options/AutoCallTip', self.autoCallTip)
+        config.WriteBool('Options/CallTipInsert', self.callTipInsert)
+        config.WriteBool('Options/UseAntiAliasing', self.GetUseAntiAliasing())
+        config.WriteBool('View/WrapMode', self.GetWrapMode())
+        config.WriteBool('View/ShowLineNumbers', self.lineNumbers)
+        config.WriteInt('View/Zoom/Shell', self.GetZoom())
+
+
+
+## NOTE: The DnD of file names is disabled until I can figure out how
+## best to still allow DnD of text.
+
+
+## #seb : File drag and drop
+## class FileDropTarget(wx.FileDropTarget):
+##     def __init__(self, obj):
+##         wx.FileDropTarget.__init__(self)
+##         self.obj = obj
+##     def OnDropFiles(self, x, y, filenames):
+##         if len(filenames) == 1:
+##             txt = 'r\"%s\"' % filenames[0]
+##         else:
+##             txt = '( '
+##             for f in filenames:
+##                 txt += 'r\"%s\" , ' % f
+##             txt += ')'
+##         self.obj.AppendText(txt)
+##         pos = self.obj.GetCurrentPos()
+##         self.obj.SetCurrentPos( pos )
+##         self.obj.SetSelection( pos, pos )
+
+
+
+## class TextAndFileDropTarget(wx.DropTarget):
+##     def __init__(self, shell):
+##         wx.DropTarget.__init__(self)
+##         self.shell = shell
+##         self.compdo = wx.DataObjectComposite()
+##         self.textdo = wx.TextDataObject()
+##         self.filedo = wx.FileDataObject()
+##         self.compdo.Add(self.textdo)
+##         self.compdo.Add(self.filedo, True)
+        
+##         self.SetDataObject(self.compdo)
+                
+##     def OnDrop(self, x, y):
+##         return True
+    
+##     def OnData(self, x, y, result):
+##         self.GetData()
+##         if self.textdo.GetTextLength() > 1:
+##             text = self.textdo.GetText()
+##             # *** Do somethign with the dragged text here...
+##             self.textdo.SetText('')
+##         else:
+##             filenames = str(self.filename.GetFilenames())
+##             if len(filenames) == 1:
+##                 txt = 'r\"%s\"' % filenames[0]
+##             else:
+##                 txt = '( '
+##                 for f in filenames:
+##                     txt += 'r\"%s\" , ' % f
+##                 txt += ')'
+##             self.shell.AppendText(txt)
+##             pos = self.shell.GetCurrentPos()
+##             self.shell.SetCurrentPos( pos )
+##             self.shell.SetSelection( pos, pos )
+
+##         return result
+