#!/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 OBBFuncts import *

ORIGIN = QPoint(0, 0)

#MIN_MOVE_REDRAW_MAGNITUDE = 4

def getNoBorderFlags():
    """try to be ass close to possible on all Qt versions"""
    noBorderFlags = Qt.WStyle_Customize \
                    | Qt.WStyle_NoBorder \
                    | Qt.WRepaintNoErase \
                    | Qt.WType_TopLevel
    try:
        noBorderFlags |= Qt.NoBackground
    except AttributeError:
        pass

    return noBorderFlags

class Floater(QWidget):
    def __init__(self, parent=None, name="Floater"):
        QWidget.__init__(self, parent, name, getNoBorderFlags())
        self.subWidgets = []
        self.revChildList = []
        self.setDrag(0)
        self.mouseReleaseActions = []
        self.setGrabber(None, None)
        try:
            self.setBackgroundMode(Qt.NoBackground)
        except AttributeError:
            #probably on Qt 2.x ...
            pass

    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):
        # set size and mask of the 1st(lower) widget
        size = self.subWidgets[0].size()
        self.setMinimumSize(size)
        self.setMaximumSize(size)
        self.region = self.subWidgets[0].opaqueRegion()
        self.setMask(self.region)
        
        # add the rest on top of it
        for widget in self.subWidgets[1:]:
            opaqueRegion = self.region
            widgetRegion = widget.opaqueRegion()
            newRegion = opaqueRegion.unite(widgetRegion)
            self.setMask(newRegion)
            self.region = newRegion
            
            size = newRegion.boundingRect().size()
            self.setMinimumSize(size)
            self.setMaximumSize(size)

        # for repaint double buffering
        self.paintBuffer = QPixmap(self.size())

    def repaintWidget(self, widget, region):
        self.update(region.boundingRect())

    def paintEvent(self, event):
        # It was a hard desision I had to take, 
        # leave the repaint slow and readable or
        # fast and ugly.
        #                        -- YGingras
        if not self.isVisible():
            return
        
        selfRect = self.rect()
        painter = QPainter()
        painter.begin(self.paintBuffer)
        
        eventRect = event.rect()
        x = eventRect.x()
        y = eventRect.y()
        width = eventRect.width()
        height = eventRect.height()

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

                widget.paintCurPixmapXY( invalidRectX - widget.x,
                                         invalidRectY - widget.y,
                                         invalidRectWidth,
                                         invalidRectHeight,
                                         0,
                                         0,
                                         painter )
        painter.end()
        painter.begin(self)
        painter.drawPixmap( event.rect().topLeft(),
                            self.paintBuffer,
                            event.rect())

    def reCenter(self):
        desktop = QApplication.desktop()
        point = QPoint( (desktop.width() - self.width())/2,
                        (desktop.height() - self.height())/2 )
        self.move(point)

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

    def isDraged(self):
        return self.draged

    def setGrabber(self, grabber, eventMapper):
        # grabber: n. an unpleasant person who grabs inconsiderately
        #
        #                                    -- WordNet (r) 1.7
        self.__grabber = grabber
        self.__grabEventMapper = eventMapper
        

    def isGrabed(self):
        return self.__grabber != None

    def drag(self, event):
        selfGeoPos = self.geometry().topLeft()
        frameGeoPos = self.frameGeometry().topLeft()
        
        geometryOffsetX = selfGeoPos.x() - frameGeoPos.x()
        geometryOffsetY = selfGeoPos.y() - frameGeoPos.y()

        mappedX = event.pos().x() - geometryOffsetX
        mappedY = event.pos().y() - geometryOffsetY

        try:
            if self.isDraged() and not qApp.hasPendingEvents():
                newPosX = event.globalPos().x() - self.lastMovePos.x()
                newPosY = event.globalPos().y() - self.lastMovePos.y()

                self.move(newPosX, newPosY)
                self.moved = 1
        except AttributeError:
            # probably on qt 2.x ...
            pass

    def startDrag(self, event):
        self.setDrag(1)
        self.dragOrigin = QPoint( event.globalPos() )
        mappedPoint = addOffset( event.pos(),
                                 pointOffset( self.geometry().topLeft(),
                                              self.frameGeometry().topLeft()))
        self.lastMovePos = mappedPoint
        self.clickPos = mappedPoint
        self.grabMouse()

    def startGrab(self, widget, eventMapper):
        self.setGrabber(widget, eventMapper)
        self.grabMouse()

    def captureMouse(self, func):
        """captureMouse : like QWidget.grabMouse() but call func when the
        mouse is released"""
        self.mouseReleaseActions.append(func)
        self.grabMouse()


    # events
    def mouseMoveEvent(self, e):
        if self.isDraged():
            return self.drag(e)

        if self.isGrabed():
            return self.__grabber.mouseMoveEvent(self.__grabEventMapper(e))

        for widget in self.revChildList:
            if widget.containsPoint(e.pos()):
                return widget.mouseMoveEvent(e)

    def mouseReleaseEvent(self, e):
        self.setDrag(0)
        self.setGrabber(None, None)
        self.releaseMouse()
        
        for widget in self.revChildList:
            if widget.containsPoint(e.pos()):
                widget.mouseReleaseEvent(e)
                break # we're done when we find the 1st matching child

        map(lambda action:action(), self.mouseReleaseActions)
        self.mouseReleaseActions = []


    def mousePressEvent(self, e):
        # TODO handle the right click
        self.moved = 0
        for widget in self.revChildList:
            if widget.containsPoint(e.pos()):
                return widget.mousePressEvent(e)

    def mouseDoubleClickEvent(self, e):
        for widget in self.revChildList:
            if widget.containsPoint(e.pos()):
                return widget.mouseDoubleClickEvent(e)

    def keyPressEvent(self, e):
        pass

    def keyReleaseEvent(self, e):
        pass

    def wheelEvent(self, e):
        for widget in self.revChildList:
            if widget.containsPoint(e.pos()):
                return widget.wheelEvent(e)
        
