# Copyright (c) 2000 Phil Thompson <phil@river-bank.demon.co.uk>


import sys
from qt import SLOT, PYSIGNAL, qApp, Qt, QMultiLineEdit, QString, QSize
from qt import QWidget, QWhatsThis


class Shell(QMultiLineEdit):
    """Shell(dbs,parent=None)

    A class that implements a user shell with which the user can enter commands
    that are executed in the remote interpreter.  dbs is the DebugServer
    instance that handles communications with the debugger client.  parent is
    the optional parent QObject.

    """
    def __init__(self,dbs,parent=None):
        QMultiLineEdit.__init__(self,parent)

        self.setWordWrap(QMultiLineEdit.WidgetWidth)
        self.setWrapPolicy(QMultiLineEdit.Anywhere)
        self.setCaption('Shell')

        QWhatsThis.add(self,
"""<b>The Shell Window</b>"""
"""<p>This is simply the Python interpreter running in a window. The"""
""" interpreter is the one that is used to run the program being debugged."""
""" This means that you can execute any Python command while the program"""
""" being debugged is running.</p>"""
"""<p>You can use the cursor keys while entering commands. There is also a"""
""" history of commands that can be recalled using the up and down cursor"""
""" keys.</p>"""
                      )

        req = QSize(500,200)
 
        if parent is not None:
            req = req.boundedTo(parent.size())
 
        self.resize(req)

        dbs.connect(dbs,PYSIGNAL('clientStatement'),self.handleClientStatement)
        dbs.connect(dbs,PYSIGNAL('clientOutput'),self.write)
        dbs.connect(dbs,PYSIGNAL('clientGone'),self.initialise)
        self.dbs = dbs

        # Initialise instance variables.
        self.initialise()
        self.history = []
        self.histidx = -1

        # Make sure we have prompts.
        try:
            sys.ps1
        except AttributeError:
            sys.ps1 = ">>> "
        try:
            sys.ps2
        except AttributeError:
            sys.ps2 = "... "

        # Display the banner.
        self.write('Python %s on %s\n' % (sys.version,sys.platform))

        self.write(sys.ps1)

    def initialise(self):
        """
        Private method to initialise ready for a new remote interpreter.
        """
        self.buf = QString()
        self.point = 0
        self.prline = 0
        self.prcol = 0
        self.inContinue = 0

    def handleClientStatement(self,more):
        """
        Private method to handle the response from the debugger client.
        """
        self.inContinue = more

        if more:
            prompt = sys.ps2
        else:
            prompt = sys.ps1

        self.write(prompt)

    def getEndPos(self):
        """
        Private method to return the line and column of the last character.
        """
        line = self.numLines() - 1
        return (line, self.lineLength(line))

    def write(self,s):
        """
        Private method to display some text.
        """
        line, col = self.getEndPos()
        self.insertAt(s,line,col)
        self.prline, self.prcol = self.getCursorPosition()

    def insertText(self,s):
        """
        Private method to insert some text at the current cursor position.
        """
        line, col = self.getCursorPosition()
        self.insertAt(s,line,col)
        self.buf.insert(self.point,s)
        self.point = self.point + s.length()

    def keyPressEvent(self,ev):
        """
        Re-implemented to handle the user input a key at a time.
        """
        txt = ev.text()
        key = ev.key()
        asc = ev.ascii()

        # See it is text to insert.
        if txt.length() and key not in (Qt.Key_Return, Qt.Key_Enter, Qt.Key_Delete, Qt.Key_Backspace) and (asc == 0 or asc >= 32):
            self.insertText(txt)
            return

        # We are not interested in these variations.
        if ev.state() & Qt.ControlButton or ev.state() & Qt.ShiftButton:
            ev.ignore()
            return

        if key == Qt.Key_Backspace:
            if self.point:
                self.backspace()
                self.point = self.point - 1
                self.buf.remove(self.point,self.point + 1)
        elif key == Qt.Key_Delete:
            self.deleteChar()
            self.buf.remove(self.point,self.point + 1)
        elif key == Qt.Key_Enter or key == Qt.Key_Return:
            line, col = self.getEndPos()
            self.setCursorPosition(line,col)
            self.newLine()

            if self.buf.isNull():
                cmd = ''
            else:
                self.history.append(QString(self.buf))
                self.histidx = -1
                cmd = str(self.buf)

            self.dbs.remoteStatement(cmd)

            self.buf.truncate(0)
            self.point = 0
        elif key == Qt.Key_Tab:
            self.insertText(txt)
        elif key == Qt.Key_Left:
            if self.point:
                self.cursorLeft()
                self.point = self.point - 1
        elif key == Qt.Key_Right:
            if self.point < self.buf.length():
                self.cursorRight()
                self.point = self.point + 1
        elif key == Qt.Key_Home:
            self.setCursorPosition(self.prline,self.prcol)
            self.point = 0
        elif key == Qt.Key_End:
            self.end()
            self.point = self.buf.length()
        elif key == Qt.Key_Up:
            if self.histidx < 0:
                self.histidx = len(self.history)

            if self.histidx > 0:
                self.histidx = self.histidx - 1
                self.useHistory()

        elif key == Qt.Key_Down:
            if self.histidx >= 0 and self.histidx < len(self.history):
                self.histidx = self.histidx + 1
                self.useHistory()
        else:
            ev.ignore()

    def useHistory(self):
        """
        Private method to display a command from the history.
        """
        if self.histidx < len(self.history):
            cmd = self.history[self.histidx]
        else:
            cmd = QString()

        self.setCursorPosition(self.prline,self.prcol)
        self.killLine()
        self.buf.truncate(0)
        self.point = 0
        self.insertText(cmd)

    def focusNextPrevChild(self,next):
        """
        Re-implement this to stop Tab moving to the next window while the user
        is entering a multi-line command.
        """
        if next and self.inContinue:
            return 0

        return QMultiLineEdit.focusNextPrevChild(self,next)

    def mousePressEvent(self,ev):
        """
        Re-implemented to suppress the right button menu and to make sure the
        cursor isn't placed before the last prompt.
        """
        return

    def mouseReleaseEvent(self,ev):
        """
        Re-implemented to suppress the middle button paste from the clipboard.
        """
        return
