# -*- coding: utf-8 -*-

"""Customized tooltips"""

import os
from PyQt4 import QtCore, QtGui
#***************************************************************************
#
#***************************************************************************
class AreaTip(QtGui.QLabel):
    """
    Customized tooltip class to show tooltips for a specified area
    under the mouse cursor.
    
    """
    
    def __init__(self, parent, showDelay=350):
        """constructor

        @param parent parent of the tooltip
        @showDelay delay in milliseconds before the tootliptp is shown        
        """
        
        
        QtGui.QLabel.__init__(self, parent, QtCore.Qt.ToolTip)
        self.hide()
        
        
        self.__data = None
        self.__showTimer = QtCore.QTimer(self)
        self.__showDelay = showDelay
        self.__rc = None

        self.connect(
                self.__showTimer, 
                QtCore.SIGNAL("timeout()"), 
                self.onShowTip)
        
        
        
        self.ensurePolished()
        frameW = self.style().pixelMetric(
                            QtGui.QStyle.PM_ToolTipLabelFrameWidth,
                            None,
                            self
                            )
        self.setMargin(    1 + frameW)
        self.setFrameStyle(QtGui.QFrame.NoFrame)
        self.setAlignment(QtCore.Qt.AlignLeft)
        self.setIndent(1)
        
        self.installEventFilter(self)

        opacity = self.style().styleHint(
                QtGui.QStyle.SH_ToolTipLabel_Opacity, 
                None, 
                self
                )
        self.setWindowOpacity( opacity / 255.0)
        self.setPalette(QtGui.QToolTip.palette())
        


    def showDelay(self):
        """returns the current show delay"""
        return self.__showDelay

    def setShowDelay(self, n):
        """adjusts the show delay
        @param n milliseconds to delay the popup of the tooltip
        """
        self.__showDelay = n
    
    
    def paintEvent(self, event):
        """overwritten QWidget.paintEvent"""
        p = QtGui.QStylePainter(self)

        opt = QtGui.QStyleOptionFrame()
        opt.init(self)
        p.drawPrimitive(QtGui.QStyle.PE_PanelTipLabel, opt)
        p.end()

        QtGui.QLabel.paintEvent(self, event)


    
    def eventFilter(self, obj, event):
        """event filter for the tooltip"""
        
        type_ = event.type()
        if type_ == event.KeyPress or type == event.KeyRelease:
            key = event.key()
            modifiers = event.modifiers()
            if modifiers & QtCore.Qt.KeyboardModifierMask or    \
                key == QtCore.Qt.Key_Shift or                               \
                key == QtCore.Qt.Key_Control or                           \
                key == QtCore.Qt.Key_Alt or                                  \
                key == QtCore.Qt.Key_Meta:
                    return False
        
        # TODO: delegate mouseactions to the window underneath the cursor
        elif type_ in (
            event.Leave,
            event.WindowActivate, 
            event.WindowDeactivate,
            event.MouseButtonPress,
            event.MouseButtonRelease,
            event.MouseButtonDblClick,
            event.FocusIn,
            event.FocusOut,
            event.Wheel,
            ):
            self.hideTip()

        return False


    
    def onShowTip(self):
        """called when the tooltip is about to be displayed"""
        if self.__data:
            pt, text, rc = self.__data

            pos = QtGui.QCursor().pos()
            if not rc.contains(pos):
                return

            
            desktop = QtGui.QApplication.desktop()
            if desktop.isVirtualDesktop():
                scr = desktop.screenNumber(pt)
            else:
                scr = desktop.screenNumber(self.parent())

            if os.name == "mac":
                screen = desktop.availableGeometry(scr)
            else:
                screen = desktop.screenGeometry(scr)

            self.setText(text)
            fm = QtGui.QFontMetrics(self.font())
            extra = QtCore.QSize(1, 0)
            # Make it look good with the default ToolTip font on Mac, 
            # which has a small descent.
            if fm.descent() == 2 and fm.ascent() >= 11:
                extra.setHeight(extra.height() + 1)
                
            self.resize(self.sizeHint() + extra)
                    
            self.setWordWrap(QtCore.Qt.mightBeRichText(text))
            self.move(pt)
            self.show()
            

    
    def hideTip(self):
        """hides the tooltip"""
        if self.__showTimer.isActive():
            self.__showTimer.stop()
        self.hide()

    
    def showText(self, pt, text, rc):
        """shows the tooltip

        @param pt point to show the tooltip at (left/top)
        @param text text to show (if emtpty string the tooltip is not shown)
        @param rc bounding rect the tooltip is assigned to
                
        """
        self.__data = (pt, text, rc)
        self.hideTip()
        if not text.isEmpty():
            self.__showTimer.start(self.__showDelay)


    def showItemTip(self, itemView, index):
        """Shows an item tip for item views (TreeView, ListView ....)
        for the specified index if necessary. 

        @param itemView item view to display the tip for
        @param index QModelIndex of the item to display the tip for

        Note: the tolltip will only be displayed if the text of the item is truncated
        or the item is not entirely visible.
                
        """
        viewport = itemView.viewport()
        rcCli = viewport.contentsRect()
        rcActual = itemView.visualRect(index)
        rcDesired = QtCore.QRect(
                                rcActual.topLeft(),
                                itemView.sizeHintForIndex(index)
                                )
        if not rcActual.contains(rcDesired, False) or \
                        not rcCli.contains(rcDesired, False):
            text = index.data()
            
            topLeft = viewport.mapToGlobal(rcActual.topLeft())
            bottomRight = viewport.mapToGlobal(rcActual.bottomRight())
            self.showText(
                topLeft,
                text.toString(),
                QtCore.QRect(topLeft, bottomRight)
                )
        

#***********************************************************************
#
#***********************************************************************
def test():
    import sys
    
        
    class TestModel(QtCore.QAbstractTableModel):
        
            
        def __init__(self, table):
            QtCore.QAbstractTableModel.__init__(self, table)

            self.__table = table
            self.__columns = ('foo', "foo")
            self.__rows = ['foo'*10 for i in range(10)]

            self.areaTip = AreaTip(table)
            
        
         
        def hasIndex(self, row, column):
            """QAbstractTableModel.hasIndex() implementation"""
            if -1 < column <= len(self.__columns):
                if -1 < row < len(self.__rows):
                    return True
            return False

        def rowCount(self, parent):
            """QAbstractTableModel.rowCount() implementation"""
            return len(self.__rows)


        def columnCount(self, parent):
            """QAbstractTableModel.columnCount() implementation"""
            return len(self.__columns)


        def headerData(self, section, orientation, role):
            """QAbstractTableModel.headerData() implementation"""
            if role == QtCore.Qt.DisplayRole and orientation == QtCore.Qt.Horizontal:
                return QtCore.QVariant(self.__columns[section])
            return QtCore.QVariant()


        def data(self, index, role):
            """QAbstractTableModel.data() implementation"""

            if self.hasIndex(index.row(), index.column()):
                if role == QtCore.Qt.DisplayRole:
                    return QtCore.QVariant(self.__rows[index.column()])
                
                # display tooltip if text does not fit column width
                # or item is not entirely visible
                elif role == QtCore.Qt.ToolTipRole:
                    self.areaTip.showItemTip(self.__table, index)
                                        
                                            
            return QtCore.QVariant()

    
    
    app = QtGui.QApplication(sys.argv)
    dlg = QtGui.QWidget()

    grid = QtGui.QHBoxLayout(dlg)

    tv = QtGui.QTableView(dlg)
    grid.addWidget(tv)

    tv.setModel(TestModel(tv))
    
    dlg.show()
    res = app.exec_()
    sys.exit(res)




#***********************************************************************
#
#***********************************************************************
if __name__ == "__main__": test()

 

