"""This file is part of the KDE project
   Copyright (C) 2001 Andrea Rizzi <rizzi@kde.org>
	              Ulrich Kuettler <ulrich.kuettler@mailbox.tu-dresden.de>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
 
   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.
 
   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
"""

from qt import *


class BasicElement:
    """The interface for every element."""
    
    def __init__(self, parent):
        self.parent = parent
        self.size = QSize()
        self.pos = QPoint()


    def x(self): return self.pos.x()
    def y(self): return self.pos.y()
    def setX(self, x): self.pos.setX(x)
    def setY(self, y): self.pos.setY(y)

    def width(self): return self.size.width()
    def height(self): return self.size.height()
    def setWidth(self, w): self.size.setWidth(w)
    def setHeight(self, h): self.size.setHeight(h)

    def globalPos(self):
        """Returns the pos in global Coords."""
        x = y = 0
        element = self
        while element != None:
            x += element.x()
            y += element.y()
            element = element.parent
        return QPoint(x, y)

    def elementAt(self, point, startPoint):
        """Returns the element that is at position point.
        `None' if there is no element there."""
        x = point.x() - startPoint.x()
        if x >= 0 and x < self.width():
            y = point.y() - startPoint.y()
            if y >= 0 and y < self.height():
                return self
    
    def moveLeft(self, cursor, fromElement):
        """Enters this element while moving to the left from
        the element `fromElement'. Searched for cursor position inside
        this element of left of it."""
        pass
    
    def moveRight(self, cursor, fromElement):
        """Enters this element while moving to the right from
        the element `fromElement'. Searched for cursor position inside
        this element of right of it."""
        pass

    def moveUp(self, cursor, fromElement):
        pass

    def moveDown(self, cursor, fromElement):
        pass
    
    def formula(self):
        """Returns the FormulaElement we are a child of."""
        return self.parent.formula()

    def draw(self, painter, styleContext, startPoint):
        """Draws the whole thing. Including its children."""
        pass

    def calcSizes(self, styleContext):
        """Recalculates the size.
        position (relative our to parent), width and height
        will be stored in self.size,
        the midline offset in self.midline.

        Please note: It's up to a parent to store its children's position."""
        pass

    def mainChild(self):
        """Returns the most important child. `None' if there is None
        child at all."""
        return None

    def setMainChild(self, sequenceElement):
        """Defines the main child."""
        pass

    def makeSequence(self):
        """Packs the element into a new SequenceElement."""
        return SequenceElement(self)

    def removeChild(self, cursor, element):
        """Removes the given child. If this happens to be the main
        child we remove ourself, too.
        The cursor has to be inside the child which is going to be
        removed."""
        pass


class SequenceElement (BasicElement):
    """The element that contains a number of children.
    The children are aligned in one line."""
    
    def __init__(self, parent):
        BasicElement.__init__(self, parent)
        self.children = []

    def elementAtCursor(self, cursor):
        """Returns the element before the cursor."""
        if cursor.pos() > 0:
            return self.children[cursor.pos()-1]
        
    def elementAt(self, point, startPoint):
        r = BasicElement.elementAt(self, point, startPoint)
        if r != None:
            for child in self.children:
                r = child.elementAt(point, QPoint(startPoint.x()+child.x(),
                                                  startPoint.y()+child.y()))
                if r != None:
                    return r
            return self

        
    def moveLeft(self, cursor, fromElement):

        # Our parent asks us for a cursor position. Found.
        if fromElement == self.parent:
            cursor.set(self, len(self.children))

        # We already owned the cursor. Ask next child then.
        elif fromElement == self:
            if cursor.pos() > 0:
                if cursor.isSelection():
                    cursor.set (self, cursor.pos()-1)
                else:
                    self.children[cursor.pos()-1].moveLeft(cursor, self)
            else:
                # Needed because FormulaElement derives this.
                if self.parent != None:
                    self.parent.moveLeft(cursor, self)

        # The cursor came from one of our children or
        # something is wrong.
        else:
            fromPos = self.children.index(fromElement)
            cursor.set(self, fromPos)
            if cursor.isSelection():
                if not cursor.mouseMark():
                    cursor.setMarkPos(fromPos+1)

    
    def moveRight(self, cursor, fromElement):

        # Our parent asks us for a cursor position. Found.
        if fromElement == self.parent:
            cursor.set(self, 0)

        # We already owned the cursor. Ask next child then.
        elif fromElement == self:
            if cursor.pos() < len(self.children):
                if cursor.isSelection():
                    cursor.set (self, cursor.pos()+1)
                else:
                    self.children[cursor.pos()].moveRight(cursor, self)
            else:
                # Needed because FormulaElement derives this.
                if self.parent != None:
                    self.parent.moveRight(cursor, self)

        # The cursor came from one of our children or
        # something is wrong.
        else:
            fromPos = self.children.index(fromElement)
            cursor.set(self, fromPos+1)
            if cursor.isSelection():
                if not cursor.mouseMark():
                    cursor.setMarkPos(fromPos)
                    

    def moveUp(self, cursor, fromElement):
        if fromElement == self.parent:
            self.moveRight(cursor, self)
        else:
            if self.parent != None:
                self.parent.moveUp(cursor, self)


    def moveDown(self, cursor, fromElement):
        if fromElement == self.parent:
            self.moveRight(cursor, self)
        else:
            if self.parent != None:
                self.parent.moveDown(cursor, self)
            

    def moveHome(self, cursor):
        if cursor.isSelection():
            element = cursor.element()
            if element != self:
                while element.parent != self:
                    element = element.parent
                cursor.setMarkPos(self.children.index(element)+1)
        cursor.set(self, 0)

    def moveEnd(self, cursor):
        if cursor.isSelection():
            element = cursor.element()
            if element != self:
                while element.parent != self:
                    element = element.parent
                cursor.setMarkPos(self.children.index(element))
        cursor.set(self, len(self.children))

        
    def draw(self, painter, styleContext, startPoint):
        x, y = startPoint.x(), startPoint.y()
        if len(self.children) > 0:
            for child in self.children:
                cX = child.x()
                cY = child.y()
                child.draw(painter, styleContext, QPoint(x+cX, y+cY))

            # Debug
            #painter.setPen(Qt.green)
            #painter.drawRect(x, y, self.width(), self.height())
        else:
            painter.setPen(Qt.blue)
            painter.drawRect(x, y, self.width(), self.height())

    def calcSizes(self, styleContext):
        if len(self.children) > 0:
            x = self.x()
            y = self.y()
            width = toMidline = fromMidline = 0
            for child in self.children:
                child.calcSizes(styleContext)
                child.setX(width)
                width += child.width()
                if child.midline > toMidline:
                    toMidline = child.midline
                if child.height()-child.midline > fromMidline:
                    fromMidline = child.height() - child.midline

            self.setWidth(width)
            self.setHeight(toMidline+fromMidline)
            self.midline = toMidline

            for child in self.children:
                child.setY(self.midline - child.midline)

        else:
            self.setWidth(10)
            self.setHeight(10)
            self.midline = 5

    def mainChild(self):
        if len(self.children) > 0:
            return self.children[0]
        return None

    def setMainChild(self, sequenceElement):
        if len(self.children) > 0:
            self.children[0] = sequenceElement
            sequenceElement.parent = self
        else:
            self.addChild(sequenceElement)

    def makeSequence(self):
        return self


    def replaceCurrentSelection(self, cursor, element):
        """Replaces the currently selected sequence (the child before
        the cursor) with the given element. The replaced sequence
        becomes the main child of the new element."""

        # it is essential to set up the parent pointer for
        # the notification to work.
        element.parent = self
        
        seq = element.makeSequence()
        if cursor.isSelection():
            f = min(cursor.pos(), cursor.markPos())
            t = max(cursor.pos(), cursor.markPos())
            for i in range(f, t):
                child = self.children.pop(f)
                self.formula().elementRemoved(child)
                seq.addChild(child)
            self.children.insert(f, element)
            cursor.setMarkPos(-1)
            cursor.set(self, f+1)
        elif cursor.pos() > 0:
            seq.addChild(self.children[cursor.pos()-1])
            self.replaceChild(cursor, element)
        else:
            self.insertChild(cursor, element)
            
        element.setMainChild(seq)


    def replaceElementByMainChild(self, cursor, element):
        """Replaces the given element with the content of its main child.
        (The main child is always a SequenceElement.)"""
        assert element.parent == self
        self.formula().elementRemoved(element)
        
        seq = element.mainChild()
        pos = self.children.index(element)
        self.children.remove(element)
        for child in seq.children:
            self.children.insert(pos, child)
            child.parent = self
            pos += 1
        cursor.set(self, pos)
        self.formula().changed()


    def addChild(self, element):
        self.children.append(element)
        element.parent = self
        self.formula().changed()

    def insertChild(self, cursor, element):
        """Inserts the new element at the cursor position.
        The cursor is placed behind the new element."""
        pos = cursor.pos()
        self.children.insert(pos, element)
        element.parent = self
        cursor.set(self, pos+1)
        self.formula().changed()
        
    def replaceChild(self, cursor, element):
        """Replaces the element before the cursor with the new one.
        No range checking. Be careful."""
        self.children[cursor.pos()-1] = element
        element.parent = self
        self.formula().changed()

    def removeChild(self, cursor, element):
        self.formula().elementRemoved(element)
        cursor.set(self, self.children.index(element))
        self.children.remove(element)
        if len(self.children) == 0:
            if self.parent != None:
                self.parent.removeChild(cursor, self)
                return
        self.formula().changed()
        
    def removeChildAt(self, cursor):
        pos = cursor.pos()
        if cursor.isSelection():
            f = min(cursor.pos(), cursor.markPos())
            t = max(cursor.pos(), cursor.markPos())
            for i in range(f, t):
                child = self.children.pop(f)
                self.formula().elementRemoved(child)
            cursor.setMarkPos(-1)
            cursor.set(self, f)
            self.formula().changed()
        elif pos < len(self.children):
            self.children.pop(pos)
            self.formula().changed()
        else:
            if len(self.children) == 0:
                if self.parent != None:
                    self.parent.removeChild(cursor, self)
            
    def removeChildBefore(self, cursor):
        pos = cursor.pos()-1
        if cursor.isSelection():
            f = min(cursor.pos(), cursor.markPos())
            t = max(cursor.pos(), cursor.markPos())
            for i in range(f, t):
                child = self.children.pop(f)
                self.formula().elementRemoved(child)
            cursor.setMarkPos(-1)
            cursor.set(self, f)
            self.formula().changed()
        elif pos >= 0:
            self.children.pop(pos)
            cursor.set(self, pos)
            self.formula().changed()
        else:
            if len(self.children) == 0:
                if self.parent != None:
                    self.parent.removeChild(cursor, self)
        
    
    def globalCursorPos(self, pos):
        """Returns the position after the child at the position
        in global Coords."""
        point = self.globalPos()
        if pos < len(self.children):
            d = self.children[pos].x()
        else:
            if len(self.children) > 0:
                d = self.width()
            else:
                d = 2
                
        point.setX(point.x()+d)
        return point

    def countChildren(self):
        return len(self.children)
            

class FormulaElement (SequenceElement):
    """The main element.
    A formula consists of a FormulaElement and its children.
    The only element that has no parent."""

    def __init__(self, document):
        SequenceElement.__init__(self, None)
        self.document = document

    def formula(self):
        return self

    def changed(self):
        """Is called by its children if the formula changed in any way."""
        self.document.changed()

    def elementRemoved(self, element):
        """Gets called just before the element is removed from the
        tree. We need this to ensure that no cursor is left in the
        leaf that gets cut off.

        Caution! The object tree must still contain the element by the time
        you call this methode."""
        self.document.elementRemoved(element)
        

class TextElement (BasicElement):
    """One char."""
    
    def __init__(self, parent, char):
        BasicElement.__init__(self, parent)
        self.char = char

    def moveLeft(self, cursor, fromElement):
        self.parent.moveLeft(cursor, self)
        
    def moveRight(self, cursor, fromElement):
        self.parent.moveRight(cursor, self)

    def draw(self, painter, styleContext, startPoint):
        styleContext.setupPainter(painter)
        painter.drawText(startPoint.x(), startPoint.y()+self.baseline, self.char)
        #painter.drawRect(startPoint.x(), startPoint.y(), self.width(), self.height())

    def calcSizes(self, styleContext):
        fm = styleContext.fontMetrics()
        self.setWidth(fm.width(self.char))
        self.setHeight(fm.height())
        self.midline = self.height() / 2
        self.baseline = fm.ascent()


class IndexElement (BasicElement):
    """Up to four indexes in the four corners."""

    def __init__(self, contentElement):
        if contentElement != None:
            BasicElement.__init__(self, contentElement.parent)
            contentElement.parent = self
        else:
            BasicElement.__init__(self, None)
            
        self.content = contentElement
        self.upperLeft = self.upperRight = None
        self.lowerLeft = self.lowerRight = None


    def elementAt(self, point, startPoint):
        r = BasicElement.elementAt(self, point, startPoint)
        if r != None:
            x, y = startPoint.x(), startPoint.y()
            r = self.content.elementAt(point, QPoint(x+self.content.x(),
                                                     y+self.content.y()))
            if r != None: return r

            if self.upperRight != None:
                r = self.upperRight.elementAt(point, QPoint(x+self.upperRight.x(),
                                                            y+self.upperRight.y()))
                if r != None: return r

            if self.upperLeft != None:
                r = self.upperLeft.elementAt(point, QPoint(x+self.upperLeft.x(),
                                                           y+self.upperLeft.y()))
                if r != None: return r

            if self.lowerRight != None:
                r = self.lowerRight.elementAt(point, QPoint(x+self.lowerRight.x(),
                                                            y+self.lowerRight.y()))
                if r != None: return r

            if self.lowerLeft != None:
                r = self.lowerLeft.elementAt(point, QPoint(x+self.lowerLeft.x(),
                                                           y+self.lowerLeft.y()))
                if r != None: return r

            return self
        
            
    def moveLeft(self, cursor, fromElement):
        assert fromElement != None

        if cursor.isSelection():
            self.parent.moveLeft(cursor, self)
            
        elif fromElement == self.parent:
            if self.lowerRight != None:
                self.lowerRight.moveLeft(cursor, self)
            elif self.upperRight != None:
                self.upperRight.moveLeft(cursor, self)
            else:
                self.content.moveLeft(cursor, self)

        elif fromElement == self.lowerRight:
            if self.upperRight != None:
                self.upperRight.moveLeft(cursor, self)
            else:
                self.content.moveLeft(cursor, self)

        elif fromElement == self.upperRight:
            self.content.moveLeft(cursor, self)

        elif fromElement == self.content:
            if self.lowerLeft != None:
                self.lowerLeft.moveLeft(cursor, self)
            elif self.upperLeft != None:
                self.upperLeft.moveLeft(cursor, self)
            else:
                self.parent.moveLeft(cursor, self)

        elif fromElement == self.lowerLeft:
            if self.upperLeft != None:
                self.upperLeft.moveLeft(cursor, self)
            else:
                self.parent.moveLeft(cursor, self)
                
        else:
            self.parent.moveLeft(cursor, self)
            
        
    def moveRight(self, cursor, fromElement):
        assert fromElement != None

        if cursor.isSelection():
            self.parent.moveRight(cursor, self)

        elif fromElement == self.parent:
            if self.upperLeft != None:
                self.upperLeft.moveRight(cursor, self)
            elif self.lowerLeft != None:
                self.lowerLeft.moveRight(cursor, self)
            else:
                self.content.moveRight(cursor, self)

        elif fromElement == self.upperLeft:
            if self.lowerLeft != None:
                self.lowerLeft.moveRight(cursor, self)
            else:
                self.content.moveRight(cursor, self)

        elif fromElement == self.lowerLeft:
            self.content.moveRight(cursor, self)

        elif fromElement == self.content:
            if self.upperRight != None:
                self.upperRight.moveRight(cursor, self)
            elif self.lowerRight != None:
                self.lowerRight.moveRight(cursor, self)
            else:
                self.parent.moveRight(cursor, self)

        elif fromElement == self.upperRight:
            if self.lowerRight != None:
                self.lowerRight.moveRight(cursor, self)
            else:
                self.parent.moveRight(cursor, self)
                
        else:
            self.parent.moveRight(cursor, self)


    def moveUp(self, cursor, fromElement):
        assert fromElement != None

        if fromElement == self.parent:
            self.content.moveRight(cursor, self)

        elif fromElement == self.upperLeft or fromElement == self.upperRight:
            self.parent.moveUp(cursor, self)

        elif fromElement == self.content:
            if self.upperRight != None:
                self.upperRight.moveRight(cursor, self)
            elif self.upperLeft != None:
                #self.upperLeft.moveRight(cursor, self)
                self.upperLeft.moveLeft(cursor, self)
            else:
                self.parent.moveUp(cursor, self)

        elif fromElement == self.lowerLeft:
            self.content.moveRight(cursor, self)

        elif fromElement == self.lowerRight:
            self.content.moveLeft(cursor, self)

        else: # should never happen.
            self.parent.moveUp(cursor, self)
                

    def moveDown(self, cursor, fromElement):
        assert fromElement != None

        if fromElement == self.parent:
            self.content.moveRight(cursor, self)

        elif fromElement == self.lowerLeft or fromElement == self.lowerRight:
            self.parent.moveDown(cursor, fromElement)

        elif fromElement == self.content:
            if self.lowerRight != None:
                self.lowerRight.moveRight(cursor, self)
            elif self.lowerLeft != None:
                #self.lowerLeft.moveRight(cursor, self)
                self.lowerLeft.moveLeft(cursor, self)
            else:
                self.parent.moveDown(cursor, self)

        elif fromElement == self.upperLeft:
            self.content.moveRight(cursor, self)

        elif fromElement == self.upperRight:
            self.content.moveLeft(cursor, self)
            
        else: # should never happen.
            self.parent.moveDown(cursor, self)

            
    def draw(self, painter, styleContext, startPoint):
        x, y = startPoint.x(), startPoint.y()
        self.content.draw(painter, styleContext,
                          QPoint(x+self.content.x(),
                                 y+self.content.y()))
        if self.upperLeft != None:
            self.upperLeft.draw(painter, styleContext,
                                QPoint(x+self.upperLeft.x(),
                                       y+self.upperLeft.y()))
        if self.upperRight != None:
            self.upperRight.draw(painter, styleContext,
                                 QPoint(x+self.upperRight.x(),
                                        y+self.upperRight.y()))
        if self.lowerLeft != None:
            self.lowerLeft.draw(painter, styleContext,
                                QPoint(x+self.lowerLeft.x(),
                                       y+self.lowerLeft.y()))
        if self.lowerRight != None:
            self.lowerRight.draw(painter, styleContext,
                                 QPoint(x+self.lowerRight.x(),
                                        y+self.lowerRight.y()))

        # Debug
        painter.setPen(Qt.red)
        painter.drawRect(x, y, self.width(), self.height())


    def calcSizes(self, styleContext):

        # get the indexes size
        if self.upperLeft != None:
            self.upperLeft.calcSizes(styleContext)
            ulWidth = self.upperLeft.width()
            ulHeight = self.upperLeft.height()
            ulMidline = self.upperLeft.midline
        else:
            ulWidth = ulHeight = ulMidline = 0

        if self.upperRight != None:
            self.upperRight.calcSizes(styleContext)
            urWidth = self.upperRight.width()
            urHeight = self.upperRight.height()
            urMidline = self.upperRight.midline
        else:
            urWidth = urHeight = urMidline = 0

        if self.lowerLeft != None:
            self.lowerLeft.calcSizes(styleContext)
            llWidth = self.lowerLeft.width()
            llHeight = self.lowerLeft.height()
            llMidline = self.lowerLeft.midline
        else:
            llWidth = llHeight = llMidline = 0

        if self.lowerRight != None:
            self.lowerRight.calcSizes(styleContext)
            lrWidth = self.lowerRight.width()
            lrHeight = self.lowerRight.height()
            lrMidline = self.lowerRight.midline
        else:
            lrWidth = lrHeight = lrMidline = 0

        # get the contents size
        self.content.calcSizes(styleContext)
        width = self.content.width()
        toMidline = self.content.midline
        fromMidline = self.content.height() - toMidline

        # calculate the x offsets
        if ulWidth > llWidth:
            self.upperLeft.setX(0)
            if self.lowerLeft != None:
                self.lowerLeft.setX(ulWidth - llWidth)
            self.content.setX(ulWidth)
            width += ulWidth
        else:
            if self.upperLeft != None:
                self.upperLeft.setX(llWidth - ulWidth)
            if self.lowerLeft != None:
                self.lowerLeft.setX(0)
            self.content.setX(llWidth)
            width += llWidth

        if self.upperRight != None:
            self.upperRight.setX(width)
        if self.lowerRight != None:
            self.lowerRight.setX(width)

        width += max(urWidth, lrWidth)
        
        # calculate the y offsets
        if ulHeight > urHeight:
            self.upperLeft.setY(0)
            if self.upperRight != None:
                self.upperRight.setY(ulHeight - urHeight)
            self.content.setY(max(ulHeight - toMidline/2, 0))
            toMidline += self.content.y()
        else:
            if self.upperLeft != None:
                self.upperLeft.setY(urHeight - ulHeight)
            if self.upperRight != None:
                self.upperRight.setY(0)
            self.content.setY(max(urHeight - toMidline/2, 0))
            toMidline += self.content.y()

        if self.lowerLeft != None:
            self.lowerLeft.setY(toMidline + fromMidline/2)
        if self.lowerRight != None:
            self.lowerRight.setY(toMidline + fromMidline/2)

        fromMidline += max(max(llHeight, lrHeight) - fromMidline/2, 0)

        # set the result
        self.setWidth(width)
        self.setHeight(toMidline+fromMidline)
        #self.midline = self.height()/2
        self.midline = toMidline


    def mainChild(self):
        return self.content

    def setMainChild(self, sequenceElement):
        self.content = sequenceElement
        self.content.parent = self
        self.formula().changed()

    def removeChild(self, cursor, element):
        if element == self.upperLeft:
            self.formula().elementRemoved(self.upperLeft)
            self.content.moveRight(cursor, self)
            self.upperLeft = None
        elif element == self.lowerLeft:
            self.formula().elementRemoved(self.lowerLeft)
            self.content.moveRight(cursor, self)
            self.lowerLeft = None
        elif element == self.upperRight:
            self.formula().elementRemoved(self.upperRight)
            self.content.moveLeft(cursor, self)
            self.upperRight = None
        elif element == self.lowerRight:
            self.formula().elementRemoved(self.lowerRight)
            self.content.moveLeft(cursor, self)
            self.lowerRight = None
        elif element == self.content:
            self.parent.removeChild(cursor, self)
            return

        if self.upperLeft == None and self.lowerLeft == None and \
           self.upperRight == None and self.lowerRight == None:
            self.parent.replaceElementByMainChild(cursor, self)
        else:
            self.formula().changed()
        

    def requireUpperLeft(self):
        if self.upperLeft == None:
            self.upperLeft = SequenceElement(self)
            self.formula().changed()
        return self.upperLeft

    def requireUpperRight(self):
        if self.upperRight == None:
            self.upperRight = SequenceElement(self)
            self.formula().changed()
        return self.upperRight

    def requireLowerLeft(self):
        if self.lowerLeft == None:
            self.lowerLeft = SequenceElement(self)
            self.formula().changed()
        return self.lowerLeft

    def requireLowerRight(self):
        if self.lowerRight == None:
            self.lowerRight = SequenceElement(self)
            self.formula().changed()
        return self.lowerRight
    
    
class Cursor:
    """The selection. This might be a one position selection or
    an area. Handles user input and object creation.
    
    Note that it is up to the elements to actually move the cursor.
    (The cursor has no chance to know how.)"""
    
    def __init__(self, formulaElement):
        self.sequenceElement = formulaElement
        self.currentPos = 0
        self.currentMarkPos = -1
        self.selectionFlag = 0
        self.mouseMarkFlag = 0

    def isSelection(self):
        return self.selectionFlag

    def mouseMark(self):
        return self.mouseMarkFlag

    def set(self, sequenceElement, pos):
        """Set the cursor to a new position."""
        if self.isSelection():
            if self.currentMarkPos == -1:
                self.currentMarkPos = self.currentPos
            if self.currentMarkPos == pos:
                self.selectionFlag = 0
        else:
            self.currentMarkPos = -1
        
        self.sequenceElement = sequenceElement
        self.currentPos = pos

    def markPos(self):
        return self.currentMarkPos
    
    def setMarkPos(self, markPos):
        """Gets called by elements if the cursor moves up to the parent."""
        self.selectionFlag = (markPos != -1)
        self.currentMarkPos = markPos
        
    def pos(self):
        return self.currentPos

    def element(self):
        return self.sequenceElement

    
    def draw(self, painter):
        point = self.sequenceElement.globalCursorPos(self.pos())
        height = self.sequenceElement.height()

        if self.isSelection():
            markPoint = self.sequenceElement.globalCursorPos(self.markPos())

            x = min(point.x(), markPoint.x())
            width = abs(point.x() - markPoint.x())
            painter.setRasterOp(Qt.XorROP)
            #painter.setRasterOp(Qt.OrROP)
            painter.fillRect(x, point.y(), width, height, QBrush(Qt.white))
            #painter.drawLine(point.x(), point.y()-2,
            #                 point.x(), point.y()+height+2)
            painter.setRasterOp(Qt.CopyROP)
        else:
            painter.setPen(Qt.blue)
            painter.drawLine(point.x(), point.y()-2,
                             point.x(), point.y()+height+2)

            

    def findIndexElement(self):
        """Looks if we are just behind an IndexElement or at the last
        position of an IndexElement's content and returns the element then."""
        element = self.sequenceElement.elementAtCursor(self)
        if isinstance(element, IndexElement):
            return element
        if self.pos() == self.sequenceElement.countChildren():
            parent = self.sequenceElement.parent
            if isinstance(parent, IndexElement):
                if self.sequenceElement == parent.mainChild():
                    return parent

                
    def addUpperRightIndex(self):
        indexElement = self.findIndexElement()
        if indexElement == None:
            indexElement = IndexElement(None)
            self.sequenceElement.replaceCurrentSelection(self, indexElement)
        index = indexElement.requireUpperRight()

        index.moveRight(self, index.parent)

    
    def addLowerRightIndex(self):
        indexElement = self.findIndexElement()
        if indexElement == None:
            indexElement = IndexElement(None)
            self.sequenceElement.replaceCurrentSelection(self, indexElement)
        index = indexElement.requireLowerRight()

        index.moveRight(self, index.parent)
        

    def addTextElement(self, char):
        textElement = TextElement(self.sequenceElement, QString(char))
        self.sequenceElement.insertChild(self, textElement)
    

    def handleKey(self, keyEvent):
        action = keyEvent.key()
        state = keyEvent.state()
        char = keyEvent.text().at(0)
        
        self.mouseMarkFlag = 0
        
        if char.isPrint():
            #self.sequenceElement.handleKey(self, char)
            latin1 = char.latin1()
            if latin1 == '[':
                #addBracketElement("[]")
                pass
            elif latin1 == '(':
                #addBracketElement(DEFAULT_DELIMITER)
                pass
            elif latin1 == '|':
                #addBracketElement("||")
                pass
            elif latin1 == '/':
                #addFractionElement(DEFAULT_FRACTION)
                pass
            elif latin1 == '@':
                #addRootElement()
                pass
            elif latin1 == '^':
                self.addUpperRightIndex()
            elif latin1 == '_':
                self.addLowerRightIndex()
            elif latin1 == ' ':
                # no space allowed.
                pass
            else:
                self.addTextElement(char)
            
        else:

            if Qt.Key_BackSpace == action:
                self.sequenceElement.removeChildBefore(self)
                return
            elif Qt.Key_Delete == action:
                self.sequenceElement.removeChildAt(self)
                return

            self.selectionFlag = state & Qt.ShiftButton
            if Qt.Key_Left == action:
                if state & Qt.ControlButton:
                    self.sequenceElement.moveHome(self)
                else:
                    self.sequenceElement.moveLeft(self, self.sequenceElement)
            elif Qt.Key_Right == action:
                if state & Qt.ControlButton:
                    self.sequenceElement.moveEnd(self)
                else:
                    self.sequenceElement.moveRight(self, self.sequenceElement)
            elif Qt.Key_Up == action:
                self.sequenceElement.moveUp(self, self.sequenceElement)
            elif Qt.Key_Down == action:
                self.sequenceElement.moveDown(self, self.sequenceElement)
            elif Qt.Key_Home == action:
                self.sequenceElement.formula().moveHome(self)
            elif Qt.Key_End == action:
                self.sequenceElement.formula().moveEnd(self)

        # Qt.Key_PageUp, Qt.Key_PageDown,
        

    def handleMousePress(self, mouseEvent):
        formula = self.sequenceElement.formula()
        element = formula.elementAt(mouseEvent.pos(), QPoint(0, 0))
        if element != None:
            if element.parent != None:
                element.moveLeft(self, element.parent)
                self.selectionFlag = 0
                self.mouseMarkFlag = 1
                self.setMarkPos(self.pos())
            #else:
            #    self.set(formula, 0)

    def handleMouseRelease(self, mouseEvent):
        self.mouseMarkFlag = 0
    
    def handleMouseMove(self, mouseEvent):
        self.selectionFlag = 1
        formula = self.sequenceElement.formula()
        element = formula.elementAt(mouseEvent.pos(), QPoint(0, 0))
        if element != None:
            if element.parent != None:
                element.parent.moveLeft(self, element)
    

    def elementRemoved(self, element):
        """The cursor must not be inside a leaf which gets cut off.
        We assume the FormulaElement will never be removed."""
        e = self.sequenceElement
        while e != None:
            if e == element:
                # This is meant to catch all cursors that did not
                # cause the deletion.
                e.parent.moveRight(self, e)
                self.sequenceElement.moveHome(self)
                return
            e = e.parent
        

class StyleContext:
    """Contains all variable information that are needed to
    draw a formula."""

    def __init__(self):
        self.font = QFont("helvetica", 18)

    def setupPainter(self, painter):
        painter.setFont(self.font)
        painter.setPen(Qt.black)

    def fontMetrics(self):
        return QFontMetrics(self.font)
    
    
class Widget(QWidget):
    """The widget that contains a formula."""
    
    def __init__(self):
        QWidget.__init__(self)
        f = self.formula = FormulaElement(self)
        self.cursor = Cursor(self.formula)
        self.styleContext = StyleContext()

        # Test data
        f.addChild(TextElement(f, "y"))
        f.addChild(TextElement(f, "="))

        s1 = SequenceElement(f)
        s1.addChild(TextElement(s1, "e"))

        i1 = IndexElement(s1)
        f.addChild(i1)

        s2 = i1.requireUpperRight()
        s2.addChild(TextElement(s2, "-"))
        s2.addChild(TextElement(s2, "o"))
        s2.addChild(TextElement(s2, "t"))

        f.addChild(TextElement(f, "("))
        f.addChild(TextElement(f, "s"))
        f.addChild(TextElement(f, "i"))
        f.addChild(TextElement(f, "n"))
        f.addChild(TextElement(f, "("))
        f.addChild(TextElement(f, "x"))
        f.addChild(TextElement(f, ")"))
        f.addChild(TextElement(f, ")"))

        s3 = SequenceElement(f)
        s3.addChild(TextElement(s3, "+"))
        s3.addChild(TextElement(s3, "f"))
        s3.addChild(TextElement(s3, "u"))
        s3.addChild(TextElement(s3, "n"))
        
        i2 = IndexElement(s3)
        i2.requireUpperLeft()
        i2.requireUpperRight()
        i2.requireLowerLeft()
        i2.requireLowerRight()
        
        f.addChild(i2)

        f.addChild(TextElement(f, ":"))
        f.addChild(TextElement(f, "-"))
        f.addChild(TextElement(f, ")"))

        s4 = SequenceElement(f)
        s4.addChild(TextElement(s4, "#"))

        i3 = IndexElement(s4)

        s5 = i3.requireUpperLeft()
        s5.addChild(TextElement(s5, "u"))
        s6 = i3.requireLowerLeft()
        s6.addChild(TextElement(s6, "d"))

        f.addChild(i3)

        self.changedFlag = 1

        
    def changed(self):
        """Gets called each time the formula changes."""
        self.changedFlag = 1
        
        
    def elementRemoved(self, element):
        """The element is going to go real soon."""
        self.cursor.elementRemoved(element)
        
        
    def paintEvent (self, e):

        if self.changedFlag:
            # You need to use the same StyleContext you use for drawing.
            self.formula.calcSizes(self.styleContext)
            self.changedFlag = 0
            
        painter = QPainter()
        painter.begin(self)
        try:
            self.formula.draw(painter, self.styleContext, QPoint(0, 0))
            self.cursor.draw(painter)
        finally:
            painter.end()


    def keyPressEvent(self, e):
        self.cursor.handleKey(e)
        self.update()
    
    def mousePressEvent(self, e):
        self.cursor.handleMousePress(e)
        self.update()
        
    def mouseReleaseEvent(self, e):
        self.cursor.handleMouseRelease(e)
        self.update()
    
    def mouseDoubleClickEvent(self, e):
        pass

    def mouseMoveEvent(self, e):
        self.cursor.handleMouseMove(e)
        self.update()
    
