#!/usr/bin/python

#   Copyright (C) 2002-2003 Yannick Gingras <ygingras@ygingras.net>
#   Copyright (C) 2002-2003 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 *
from OBBUtils.MultiFunct import MultiFunct

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.x = x
        self.y = y
        self.updateMask()

        # save a few instanciations
        self.tempPoint = QPoint(0, 0)
        
        # once we are full grown, we can proudly show in the face of our parent
        if parent:
            parent.addSubWidget(self)


    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.x, self.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.x, self.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
        oldX = self.x
        oldY = self.y
        oldRegion = QRegion(self.region)
        
        self.x = x
        self.y = y
        self.region.translate( x - oldX,
                               y - oldY )
        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.curBoundingRect.size()


    def width(self):
        return self.curWidth


    def height(self):
        return self.curHeight


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

        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 ):
        # See the comment in Floater.paintEvent()
        statePix = self.pixmapSet.getState(self.curState)

        globalPosX = globalX + self.x
        globalPosY = globalY + self.y

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

        for widget in self.subWidgets:
            ( isIn,
              invalidRectX,
              invalidRectY,
              invalidRectX2,
              invalidRectY2 ) = widget.getIntersection(x, y, width, height)
            if isIn:
                widgetRect = widget.curBoundingRect

                invalidRectWidth  = invalidRectX2 - invalidRectX
                invalidRectHeight = invalidRectY2 - invalidRectY
        
                widget.paintCurPixmapXY( invalidRectX - widget.x,
                                         invalidRectY - widget.y,
                                         invalidRectWidth,
                                         invalidRectHeight,
                                         globalPosX,
                                         globalPosY,
                                         paintDev )


    def getIntersection(self, x, y, width, height):
        # original : 
        #return ( ( max(sx, x) <= \
        #           min(self.curWidth+sx, width+x) ) and \
        #         ( max(sy, y) <= \
        #           min(self.curHeight+sy, height+y) ) )

        # optimized :
        sDX = self.curWidth+self.x
        sDY = self.curHeight+self.y
        dX = width+x
        dY = height+y
        if self.x > x:
            x = self.x
        if self.y > y:
            y = self.y
        if sDX < dX:
            dX = sDX
        if sDY < dY:
            dY = sDY

        return (x <= dX and y <= dY, x, y, dX, dY)


    def containsPoint(self, point):
        if self.region.contains(point):
            return 1
        return 0


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

    def repaintWidget(self, widget, region):
        # remap region
        newRegion = QRegion(region)
        newRegion.translate(self.x, self.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)

    def startGrab(self, widget=None, eventMapper=None):
        if not eventMapper:
            eventMapper = MultiFunct()

        if not widget:
            widget=self
            
        eventMapper.compose(self.mapMouseEvent)
        self.parent().startGrab(widget, eventMapper)

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

    def mapPointToSelf(self, point):
        return pointOffset(point, QPoint(self.x, self.y))

    def mapPointToParent(self, point):
        return addOffset(point, QPoint(self.x, self.y))

    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,))


