
#   Copyright (C) 2002 Yannick Gingras <ygingras@ygingras.net>
#   Copyright (C) 2002 Vincent Barbin <vbarbin@openbeatbox.org>

#   This file is part of Open Beat Box.

#   Open Beat Box is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.

#   Open Beat Box 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 General Public License for more details.

#   You should have received a copy of the GNU General Public License
#   along with Open Beat Box; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


from qt import *
from guiPath import *
from OBBConstants import *
from PixmapSet import *
from OBBFuncts import *

ORIGIN = QPoint(0, 0)

class OBBWidget(QObject):
    def __init__(self, pixmapSet, parent=None, x=0, y=0):
        QObject.__init__(self, parent)
        self.pixmapSet = pixmapSet
        self.subWidgets = []
        self.revChildList = []
        self.curState = DISABLED
        self.pos = QPoint(x, y)
        self.updateMask()

        # save a few instanciations
        self.tempPoint = QPoint(0, 0)

    def addSubWidget(self, widget):
        # stacking order for repaints
        self.subWidgets.append(widget)
        self.updateMask()

        # inverse order for events
        self.revChildList = list(self.subWidgets)
        self.revChildList.reverse()

    def updateMask(self):
        mask = self.pixmapSet.getState(DISABLED).mask()
        self.region = QRegion(mask)
        self.region.translate(self.pos.x(), self.pos.y())

        self.rect = self.region.boundingRect()

        for widget in self.subWidgets:
            opaqueRegion = self.region
            widgetRegion = QRegion(widget.opaqueRegion())
            # map to local coords
            widgetRegion.translate(self.pos.x(), self.pos.y())
            newRegion = opaqueRegion.unite(widgetRegion)
            
            self.region = newRegion
        self.curBoundingRect = self.region.boundingRect()
        self.curWidth = self.curBoundingRect.width()
        self.curHeight = self.curBoundingRect.height()

    def move(self, x, y):
        """move to (x, t) inside parent().  Does NOT
        handle stepping outside of parents region wich would require a
        SLOW mask recalculation.  """
        # TODO : check if we step outside of parent
        oldPos = self.pos
        oldRegion = QRegion(self.region)
        
        self.pos = QPoint(x, y)
        self.region.translate( x - oldPos.x(),
                               y - oldPos.y() )
        self.curBoundingRect = self.region.boundingRect()
        
        self.parent().repaintWidget(self, self.region.unite(oldRegion))
        

    def boundingRect(self):
        return self.curBoundingRect
            
    def opaqueRegion(self):
        return self.region

    def size(self):
        return self.boundingRect().size()

    def width(self):
        return self.curWidth

    def height(self):
        return self.curHeight

    def topLeft(self):
        return self.pos

    def setState(self, state):
        if self.curState == state:
            return

        #self.__pixmapChanged = 1
        self.curState = state
        self.emit(PYSIGNAL("stateChanged()"), ())
        self.parent().repaintWidget(self, self.region)

    def toggleState(self):
        if self.curState == ACTIVATED:
            self.setState(DESACTIVATED)
        else:
            self.setState(ACTIVATED)

    def paintCurPixmapXY( self,
                          x,
                          y,
                          width,
                          height,
                          globalX,
                          globalY,
                          paintDev ):
        
        statePix = self.pixmapSet.getState(self.curState)

        globalPosX = globalX + self.pos.x()
        globalPosY = globalY + self.pos.y()

        paintDev.drawPixmap( x+globalPosX,
                             y+globalPosY,
                             statePix,
                             x,
                             y,
                             width,
                             height )

        for widget in self.subWidgets:
            if widget.intersectsXY(x, y, width, height):
                self.paintChildPixmapXY( x,
                                         y,
                                         width,
                                         height,
                                         globalPosX,
                                         globalPosY,
                                         paintDev,
                                         widget )


    def paintChildPixmapXY( self,
                            x,
                            y,
                            width,
                            height,
                            globalX,
                            globalY,
                            paintDev,
                            widget ):
        # remapping is not just confusing, it's also
        # really slow and we must do it inline
        #                          -- YGingras
        widgetX = widget.topLeft().x()
        widgetY = widget.topLeft().y()
        widgetRect = widget.boundingRect()
        
        invalidRectX = max(x, widgetX)
        invalidRectY = max(y, widgetY)
        invalidRectX2 = min(x+width, widgetX+widget.width())
        invalidRectY2 = min(y+height, widgetY+widget.height())
        invalidRectWidth = invalidRectX2 - invalidRectX
        invalidRectHeight = invalidRectY2 - invalidRectY

        mappedX = invalidRectX - widgetX
        mappedY = invalidRectY - widgetY
        
        widget.paintCurPixmapXY( mappedX,
                                 mappedY,
                                 invalidRectWidth,
                                 invalidRectHeight,
                                 globalX,
                                 globalY,
                                 paintDev )

    def containsPoint(self, point):
        if self.region.contains(point):
            return 1
        
        for widget in self.subWidgets:
            # map to local coords
            self.tempPoint.setX(point.x() - self.pos.x())
            self.tempPoint.setY(point.y() - self.pos.y())
            if widget.containsPoint(self.tempPoint):
                return 1
        return 0

    def intersects(self, rect):
        return self.curBoundingRect.intersects(rect)

    def intersectsXY(self, x, y, width, height):
        # self coords
        sx = self.curBoundingRect.x()
        sy = self.curBoundingRect.y()
        return ( ( max(sx, x) <= \
                   min(self.curWidth+sx, width+x) ) and \
                 ( max(sy, y) <= \
                   min(self.curHeight+sy, height+y) ) )
        #return self.intersects(QRect(x, y, width, height))
        

    #######################
    # parent forwarding
    #######################

    def repaintWidget(self, widget, region):
        # remap region
        newRegion = QRegion(region)
        newRegion.translate(self.pos.x(), self.pos.y())
        
        # call parent
        self.parent().repaintWidget(self, newRegion)

    def captureMouse(self, func=None):
        """captureMouse : Ensure that func will be called when the
        mouse is released."""
        self.parent().captureMouse(func)

    def releaseMouse(self):
        self.parent().releaseMouse()

    def setDrag(self, isDraged=1):
        self.parent().setDrag(isDraged)

    def isDraged(self):
        return self.parent().isDraged()

    def drag(self, e):
        if qApp.hasPendingEvents():
            return
        mappedEvent = QMouseEvent( e.type(),
                                   self.mapPointToParent(e.pos()),
                                   e.button(),
                                   e.state() )

        self.parent().drag(mappedEvent)

    def startDrag(self, e):
        mappedEvent = QMouseEvent( e.type(),
                                   self.mapPointToParent(e.pos()),
                                   e.button(),
                                   e.state() )

        self.parent().startDrag(mappedEvent)


    #######################
    # mapping
    #######################

    def mapPointToSelf(self, point):
        return pointOffset(point, self.pos)

    def mapPointToParent(self, point):
        return addOffset(point, self.pos)

    def mapMouseEvent(self, e):
        return QMouseEvent( e.type(),
                            self.mapPointToSelf(e.pos()),
                            e.button(),
                            e.state() )

    def mapWheelEvent(self, e):
        return QWheelEvent( self.mapPointToSelf(e.pos()),
                            e.delta(),
                            e.state(),
                            e.orientation() )

    
    #######################
    # events
    #######################
    
    def mousePressEvent(self, e):
        mappedEvent = self.mapMouseEvent(e)

        for widget in self.revChildList:
            if widget.containsPoint(mappedEvent.pos()):
                return widget.mousePressEvent(mappedEvent)
        
        self.emit(PYSIGNAL('beingPressed(e)'), (e,))

    def mouseReleaseEvent(self, e):
        mappedEvent = self.mapMouseEvent(e)

        for widget in self.revChildList:
            if widget.containsPoint(mappedEvent.pos()):
                return widget.mouseReleaseEvent(mappedEvent)
        
        self.emit(PYSIGNAL('beingReleased(e)'), (e,))

    
    def mouseDoubleClickEvent(self, e):
        mappedEvent = self.mapMouseEvent(e)

        for widget in self.revChildList:
            if widget.containsPoint(mappedEvent.pos()):
                return widget.mouseDoubleClickEvent(mappedEvent)
        
        self.emit(PYSIGNAL('beingDblClicked(e)'), (e,))


    def mouseMoveEvent(self, e):
        mappedEvent = self.mapMouseEvent(e)

        for widget in self.revChildList:
            if widget.containsPoint(mappedEvent.pos()):
                return widget.mouseMoveEvent(mappedEvent)
        
        self.emit(PYSIGNAL('beingHovered(e)'), (e,))


    def keyPressEvent(self, e):
        pass

    def keyReleaseEvent(self, e):
        pass

    def wheelEvent(self, e):
        mappedEvent = self.mapWheelEvent(e)

        for widget in self.revChildList:
            if widget.containsPoint(mappedEvent.pos()):
                return widget.wheelEvent(mappedEvent)
        
        self.emit(PYSIGNAL('beingWheeled(e)'), (e,))

