# -*- coding: utf-8 -*-
#
# Licensed under the terms of the Qwt License
# Copyright (c) 2002 Uwe Rathmann, for the original C++ code
# Copyright (c) 2015 Pierre Raybaut, for the Python translation/optimization
# (see LICENSE file for more details)

"""
QwtLegend
---------

.. autoclass:: QwtLegendData
   :members:

.. autoclass:: QwtLegendLabel
   :members:

.. autoclass:: QwtLegend
   :members:
"""

import numpy as np

from qwt.qt.QtGui import (QFrame, QScrollArea, QWidget, QVBoxLayout, QPalette,
                          QApplication, QStyleOption, QStyle, QPixmap,
                          QPainter, qDrawWinButton)
from qwt.qt.QtCore import Signal, QEvent, QSize, Qt, QRect, QRectF, QPoint

from qwt.text import QwtText, QwtTextLabel
from qwt.dyngrid_layout import QwtDynGridLayout
from qwt.painter import QwtPainter


class QwtLegendData(object):
    """
    Attributes of an entry on a legend
    
    `QwtLegendData` is an abstract container ( like `QAbstractModel` )
    to exchange attributes, that are only known between to 
    the plot item and the legend. 
      
    By overloading `QwtPlotItem.legendData()` any other set of attributes
    could be used, that can be handled by a modified ( or completely 
    different ) implementation of a legend.
    
    .. seealso::
    
        :py:class:`qwt.legend.QwtLegend`
    
    .. note::
    
        The stockchart example implements a legend as a tree 
        with checkable items
    """
    
    # enum Mode
    ReadOnly, Clickable, Checkable = list(range(3))
    
    # enum Role
    ModeRole, TitleRole, IconRole = list(range(3))
    UserRole = 32
    
    def __init__(self):
        self.__map = {}
    
    def setValues(self, map_):
        """
        Set the legend attributes
        
        :param dict map\_: Values

        .. seealso::
        
            :py:meth:`values()`
        """
        self.__map = map_
    
    def values(self):
        """
        :return: Legend attributes

        .. seealso::
        
            :py:meth:`setValues()`
        """
        return self.__map
    
    def hasRole(self, role):
        """
        :param int role: Attribute role
        :return: True, when the internal map has an entry for role
        """
        return role in self.__map
        
    def setValue(self, role, data):
        """
        Set an attribute value
        
        :param int role: Attribute role
        :param QVariant data: Attribute value

        .. seealso::
        
            :py:meth:`value()`
        """
        self.__map[role] = data
    
    def value(self, role):
        """
        :param int role: Attribute role
        :return: Attribute value for a specific role

        .. seealso::
        
            :py:meth:`setValue()`
        """
        return self.__map.get(role)
    
    def isValid(self):
        """
        :return: True, when the internal map is empty
        """
        return len(self.__map) != 0
    
    def title(self):
        """
        :return: Value of the TitleRole attribute
        """
        titleValue = self.value(QwtLegendData.TitleRole)
        if isinstance(titleValue, QwtText):
            text = titleValue
        else:
            text.setText(titleValue)
        return text
    
    def icon(self):
        """
        :return: Value of the IconRole attribute
        """
        return self.value(QwtLegendData.IconRole)
    
    def mode(self):
        """
        :return: Value of the ModeRole attribute
        """
        modeValue = self.value(QwtLegendData.ModeRole)
        if isinstance(modeValue, int):
            return modeValue
        return QwtLegendData.ReadOnly



BUTTONFRAME = 2
MARGIN = 2


def buttonShift(w):
    option = QStyleOption()
    option.initFrom(w)
    ph = w.style().pixelMetric(QStyle.PM_ButtonShiftHorizontal, option, w)
    pv = w.style().pixelMetric(QStyle.PM_ButtonShiftVertical, option, w)
    return QSize(ph, pv)


class QwtLegendLabel_PrivateData(object):
    def __init__(self):
        self.itemMode = QwtLegendData.ReadOnly
        self.isDown = False
        self.spacing = MARGIN
        self.legendData = QwtLegendData()
        self.icon = QPixmap()
        

class QwtLegendLabel(QwtTextLabel):
    """A widget representing something on a QwtLegend."""
    
    clicked = Signal()
    pressed = Signal()
    released = Signal()
    checked = Signal(bool)
    
    def __init__(self, parent=None):
        QwtTextLabel.__init__(self, parent)
        self.__data = QwtLegendLabel_PrivateData()
        self.setMargin(MARGIN)
        self.setIndent(MARGIN)
        
    def setData(self, legendData):
        """
        Set the attributes of the legend label
        
        :param QwtLegendData legendData: Attributes of the label

        .. seealso::
        
            :py:meth:`data()`
        """
        self.__data.legendData = legendData
        doUpdate = self.updatesEnabled()
        self.setUpdatesEnabled(False)
        self.setText(legendData.title())
        icon = legendData.icon()
        if icon is not None:
            self.setIcon(icon.toPixmap())
        if legendData.hasRole(QwtLegendData.ModeRole):
            self.setItemMode(legendData.mode())
        if doUpdate:
            self.setUpdatesEnabled(True)
            self.update()
    
    def data(self):
        """
        :return: Attributes of the label

        .. seealso::
        
            :py:meth:`setData()`, :py:meth:`qwt.plot.QwtPlotItem.legendData()`
        """
        return self.__data.legendData
    
    def setText(self, text):
        """
        Set the text to the legend item
        
        :param qwt.text.QwtText text: Text label

        .. seealso::
        
            :py:meth:`text()`
        """
        flags = Qt.AlignLeft|Qt.AlignVCenter|Qt.TextExpandTabs|Qt.TextWordWrap
        txt = text  #TODO: WTF?
        txt.setRenderFlags(flags)
        QwtTextLabel.setText(self, text)
    
    def setItemMode(self, mode):
        """
        Set the item mode.
        The default is `QwtLegendData.ReadOnly`.
        
        :param int mode: Item mode

        .. seealso::
        
            :py:meth:`itemMode()`
        """
        if mode != self.__data.itemMode:
            self.__data.itemMode = mode
            self.__data.isDown = False
            self.setFocusPolicy(Qt.TabFocus if mode != QwtLegendData.ReadOnly
                                else Qt.NoFocus)
            self.setMargin(BUTTONFRAME+MARGIN)
            self.updateGeometry()
    
    def itemMode(self):
        """
        :return: Item mode

        .. seealso::
        
            :py:meth:`setItemMode()`
        """
        return self.__data.itemMode
    
    def setIcon(self, icon):
        """
        Assign the icon
        
        :param QPixmap icon: Pixmap representing a plot item

        .. seealso::
        
            :py:meth:`icon()`, :py:meth:`qwt.plot.QwtPlotItem.legendIcon()`
        """
        self.__data.icon = icon
        indent = self.margin()+self.__data.spacing
        if icon.width() > 0:
            indent += icon.width()+self.__data.spacing
        self.setIndent(indent)
    
    def icon(self):
        """
        :return: Pixmap representing a plot item

        .. seealso::
        
            :py:meth:`setIcon()`
        """
        return self.__data.icon
    
    def setSpacing(self, spacing):
        """
        Change the spacing between icon and text
        
        :param int spacing: Spacing

        .. seealso::
        
            :py:meth:`spacing()`, :py:meth:`qwt.text.QwtTextLabel.margin()`
        """
        spacing = max([spacing, 0])
        if spacing != self.__data.spacing:
            self.__data.spacing = spacing
            indent = self.margin()+self.__data.spacing
            if self.__data.icon.width() > 0:
                indent += self.__data.icon.width()+self.__data.spacing
            self.setIndent(indent)
    
    def spacing(self):
        """
        :return: Spacing between icon and text

        .. seealso::
        
            :py:meth:`setSpacing()`
        """
        return self.__data.spacing
    
    def setChecked(self, on):
        """
        Check/Uncheck a the item
        
        :param bool on: check/uncheck

        .. seealso::
        
            :py:meth:`isChecked()`, :py:meth:`setItemMode()`
        """
        if self.__data.itemMode == QwtLegendData.Checkable:
            isBlocked = self.signalsBlocked()
            self.blockSignals(True)
            self.setDown(on)
            self.blockSignals(isBlocked)
    
    def isChecked(self):
        """
        :return: true, if the item is checked

        .. seealso::
        
            :py:meth:`setChecked()`
        """
        return self.__data.itemMode == QwtLegendData.Checkable and self.isDown()
    
    def setDown(self, down):
        """
        Set the item being down
        
        :param bool on: true, if the item is down

        .. seealso::
        
            :py:meth:`isDown()`
        """
        if down == self.__data.isDown:
            return
        self.__data.isDown = down
        self.update()
        if self.__data.itemMode == QwtLegendData.Clickable:
            if self.__data.isDown:
                self.pressed.emit()
            else:
                self.released.emit()
                self.clicked.emit()
        if self.__data.itemMode == QwtLegendData.Checkable:
            self.checked.emit(self.__data.isDown)
    
    def isDown(self):
        """
        :return: true, if the item is down

        .. seealso::
        
            :py:meth:`setDown()`
        """
        return self.__data.isDown
    
    def sizeHint(self):
        """
        :return: a size hint
        """
        sz = QwtTextLabel.sizeHint(self)
        sz.setHeight(max([sz.height(), self.__data.icon.height()+4]))
        if self.__data.itemMode != QwtLegendData.ReadOnly:
            sz += buttonShift(self)
            sz = sz.expandedTo(QApplication.globalStrut())
        return sz
    
    def paintEvent(self, e):
        cr = self.contentsRect()
        painter = QPainter(self)
        painter.setClipRegion(e.region())
        if self.__data.isDown:
            qDrawWinButton(painter, 0, 0, self.width(), self.height(),
                           self.palette(), True)
        painter.save()
        if self.__data.isDown:
            shiftSize = buttonShift(self)
            painter.translate(shiftSize.width(), shiftSize.height())
        painter.setClipRect(cr)
        self.drawContents(painter)
        if not self.__data.icon.isNull():
            iconRect = QRect(cr)
            iconRect.setX(iconRect.x()+self.margin())
            if self.__data.itemMode != QwtLegendData.ReadOnly:
                iconRect.setX(iconRect.x()+BUTTONFRAME)
            iconRect.setSize(self.__data.icon.size())
            iconRect.moveCenter(QPoint(iconRect.center().x(),
                                       cr.center().y()))
            painter.drawPixmap(iconRect, self.__data.icon)
        painter.restore()

    def mousePressEvent(self, e):
        if e.button() == Qt.LeftButton:
            if self.__data.itemMode == QwtLegendData.Clickable:
                self.setDown(True)
                return
            elif self.__data.itemMode == QwtLegendData.Checkable:
                self.setDown(not self.isDown())
                return
        QwtTextLabel.mousePressEvent(self, e)
    
    def mouseReleaseEvent(self, e):
        if e.button() == Qt.LeftButton:
            if self.__data.itemMode == QwtLegendData.Clickable:
                self.setDown(False)
                return
            elif self.__data.itemMode == QwtLegendData.Checkable:
                return
        QwtTextLabel.mouseReleaseEvent(self, e)

    def keyPressEvent(self, e):
        if e.key() == Qt.Key_Space:
            if self.__data.itemMode == QwtLegendData.Clickable:
                if not e.isAutoRepeat():
                    self.setDown(True)
                return
            elif self.__data.itemMode == QwtLegendData.Checkable:
                if not e.isAutoRepeat():
                    self.setDown(not self.isDown())
                return
        QwtTextLabel.keyPressEvent(self, e)

    def keyReleaseEvent(self, e):
        if e.key() == Qt.Key_Space:
            if self.__data.itemMode == QwtLegendData.Clickable:
                if not e.isAutoRepeat():
                    self.setDown(False)
                return
            elif self.__data.itemMode == QwtLegendData.Checkable:
                return
        QwtTextLabel.keyReleaseEvent(self, e)



class QwtAbstractLegend(QFrame):
    def __init__(self, parent):
        QFrame.__init__(self, parent)
        
    def renderLegend(self, painter, rect, fillBackground):
        raise NotImplementedError
    
    def isEmpty(self):
        return 0
        
    def scrollExtent(self, orientation):
        return 0
    
    def updateLegend(self, itemInfo, data):
        raise NotImplementedError        


class Entry(object):
    def __init__(self):
        self.itemInfo = None
        self.widgets = []

class QwtLegendMap(object):
    def __init__(self):
        self.__entries = []
    
    def isEmpty(self):
        return len(self.__entries) == 0
    
    def insert(self, itemInfo, widgets):
        for entry in self.__entries:
            if entry.itemInfo == itemInfo:
                entry.widgets = widgets
                return
        newEntry = Entry()
        newEntry.itemInfo = itemInfo
        newEntry.widgets = widgets
        self.__entries += [newEntry]
        
    def remove(self, itemInfo):
        for entry in self.__entries[:]:
            if entry.itemInfo == itemInfo:
                self.__entries.remove(entry)
                return
    
    def removeWidget(self, widget):
        for entry in self.__entries:
            while widget in entry.widgets:
                entry.widgets.remove(widget)
    
    def itemInfo(self, widget):
        if widget is not None:
            for entry in self.__entries:
                if widget in entry.widgets:
                    return entry.itemInfo
    
    def legendWidgets(self, itemInfo):
        if itemInfo is not None:
            for entry in self.__entries:
                if entry.itemInfo == itemInfo:
                    return entry.widgets
        return []
    

class LegendView(QScrollArea):
    def __init__(self, parent):
        QScrollArea.__init__(self, parent)
        self.gridLayout = None
        self.contentsWidget = QWidget(self)
        self.contentsWidget.setObjectName("QwtLegendViewContents")
        self.setWidget(self.contentsWidget)
        self.setWidgetResizable(False)
        self.viewport().setObjectName("QwtLegendViewport")
        self.contentsWidget.setAutoFillBackground(False)
        self.viewport().setAutoFillBackground(False)
    
    def event(self, event):
        if event.type() == QEvent.PolishRequest:
            self.setFocusPolicy(Qt.NoFocus)
        if event.type() == QEvent.Resize:
            cr = self.contentsRect()
            w = cr.width()
            h = self.contentsWidget.heightForWidth(cr.width())
            if h > w:
                w -= self.verticalScrollBar().sizeHint().width()
                h = self.contentsWidget.heightForWidth(w)
            self.contentsWidget.resize(w, h)
        return QScrollArea.event(self, event)
    
    def viewportEvent(self, event):
        ok = QScrollArea.viewportEvent(self, event)
        if event.type() == QEvent.Resize:
            self.layoutContents()
        return ok
    
    def viewportSize(self, w, h):
        sbHeight = self.horizontalScrollBar().sizeHint().height()
        sbWidth = self.verticalScrollBar().sizeHint().width()
        cw = self.contentsRect().width()
        ch = self.contentsRect().height()
        vw = cw
        vh = ch
        if w > vw:
            vh -= sbHeight
        if h > vh:
            vw -= sbWidth
            if w > vw and vh == ch:
                vh -= sbHeight
        return QSize(vw, vh)
    
    def layoutContents(self):
        tl = self.gridLayout
        if tl is None:
            return
        visibleSize = self.viewport().contentsRect().size()
        margins = tl.contentsMargins()
        margin_w = margins.left() + margins.right()
        minW = int(tl.maxItemWidth()+margin_w)
        w = max([visibleSize.width(), minW])
        h = max([tl.heightForWidth(w), visibleSize.height()])
        vpWidth = self.viewportSize(w, h).width()
        if w > vpWidth:
            w = max([vpWidth, minW])
            h = max([tl.heightForWidth(w), visibleSize.height()])
        self.contentsWidget.resize(w, h)
        

class QwtLegend_PrivateData(object):
    def __init__(self):
        self.itemMode = QwtLegendData.ReadOnly
        self.view = None
        self.itemMap = QwtLegendMap()    

class QwtLegend(QwtAbstractLegend):
    """
    The legend widget

    The QwtLegend widget is a tabular arrangement of legend items. Legend
    items might be any type of widget, but in general they will be
    a QwtLegendLabel.

    .. seealso ::
    
        :py:class`qwt.legend.QwtLegendLabel`, 
        :py:class`qwt.plot.QwtPlotItem`, 
        :py:class`qwt.plot.QwtPlot`
        
    .. py:class:: QwtLegend([parent=None])
    
        Constructor
        
        :param QWidget parent: Parent widget

    .. py:data:: clicked

        A signal which is emitted when the user has clicked on
        a legend label, which is in `QwtLegendData.Clickable` mode.

        :param itemInfo: Info for the item item of the selected legend item
        :param index: Index of the legend label in the list of widgets that are associated with the plot item
        
        .. note::
            
            Clicks are disabled as default

    .. py:data:: checked
    
        A signal which is emitted when the user has clicked on
        a legend label, which is in `QwtLegendData.Checkable` mode

        :param itemInfo: Info for the item of the selected legend label
        :param index: Index of the legend label in the list of widgets that are associated with the plot item
        :param on: True when the legend label is checked
        
        .. note::
            
            Clicks are disabled as default
    """

    clicked = Signal("PyQt_PyObject", int)
    checked = Signal("PyQt_PyObject", bool, int)
    
    def __init__(self, parent=None):
        QwtAbstractLegend.__init__(self, parent)
        self.setFrameStyle(QFrame.NoFrame)
        self.__data = QwtLegend_PrivateData()
        self.__data.view = LegendView(self)
        self.__data.view.setObjectName("QwtLegendView")
        self.__data.view.setFrameStyle(QFrame.NoFrame)
        gridLayout = QwtDynGridLayout(self.__data.view.contentsWidget)
        gridLayout.setAlignment(Qt.AlignHCenter|Qt.AlignTop)
        self.__data.view.gridLayout = gridLayout
        self.__data.view.contentsWidget.installEventFilter(self)
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.__data.view)
    
    def setMaxColumns(self, numColumns):
        """
        Set the maximum number of entries in a row

        F.e when the maximum is set to 1 all items are aligned
        vertically. 0 means unlimited
        
        :param int numColumns: Maximum number of entries in a row

        .. seealso::
        
            :py:meth:`maxColumns()`, 
            :py:meth:`QwtDynGridLayout.setMaxColumns()`
        """
        tl = self.__data.view.gridLayout
        if tl is not None:
            tl.setMaxColumns(numColumns)
    
    def maxColumns(self):
        """
        :return: Maximum number of entries in a row

        .. seealso::
        
            :py:meth:`setMaxColumns()`, 
            :py:meth:`QwtDynGridLayout.maxColumns()`
        """
        tl = self.__data.view.gridLayout
        if tl is not None:
            return tl.maxColumns()
        return 0
    
    def setDefaultItemMode(self, mode):
        """
        Set the default mode for legend labels

        Legend labels will be constructed according to the
        attributes in a `QwtLegendData` object. When it doesn't
        contain a value for the `QwtLegendData.ModeRole` the
        label will be initialized with the default mode of the legend.
        
        :param int mode: Default item mode

        .. seealso::
        
            :py:meth:`itemMode()`, 
            :py:meth:`QwtLegendData.value()`, 
            :py:meth:`QwtPlotItem::legendData()`
        
        ... note::
        
            Changing the mode doesn't have any effect on existing labels.
        """
        self.__data.itemMode = mode
    
    def defaultItemMode(self):
        """
        :return: Default item mode

        .. seealso::
        
            :py:meth:`setDefaultItemMode()`
        """
        return self.__data.itemMode
        
    def contentsWidget(self):
        """
        The contents widget is the only child of the viewport of 
        the internal `QScrollArea` and the parent widget of all legend 
        items.
  
        :return: Container widget of the legend items
        """
        return self.__data.view.contentsWidget
    
    def horizontalScrollBar(self):
        """
        :return: Horizontal scrollbar

        .. seealso::
        
            :py:meth:`verticalScrollBar()`
        """
        return self.__data.view.horizontalScrollBar()
    
    def verticalScrollBar(self):
        """
        :return: Vertical scrollbar

        .. seealso::
        
            :py:meth:`horizontalScrollBar()`
        """
        return self.__data.view.verticalScrollBar()
    
    def updateLegend(self, itemInfo, data):
        """
        Update the entries for an item
        
        :param QVariant itemInfo: Info for an item
        :param list data: Default item mode
        """
        widgetList = self.legendWidgets(itemInfo)
        if len(widgetList) != len(data):
            contentsLayout = self.__data.view.gridLayout
            while len(widgetList) > len(data):
                w = widgetList.pop(-1)
                contentsLayout.removeWidget(w)
                w.hide()
                w.deleteLater()
            for i in range(len(widgetList), len(data)):
                widget = self.createWidget(data[i])
                if contentsLayout is not None:
                    contentsLayout.addWidget(widget)
                if self.isVisible():
                    widget.setVisible(True)
                widgetList.append(widget)
            if not widgetList:
                self.__data.itemMap.remove(itemInfo)
            else:
                self.__data.itemMap.insert(itemInfo, widgetList)
            self.updateTabOrder()
        for i in range(len(data)):
            self.updateWidget(widgetList[i], data[i])
    
    def createWidget(self, data):
        """
        Create a widget to be inserted into the legend

        The default implementation returns a `QwtLegendLabel`.
        
        :param QwtLegendData data: Attributes of the legend entry
        :return: Widget representing data on the legend
        
        ... note::
        
            updateWidget() will called soon after createWidget()
            with the same attributes.
        """
        label = QwtLegendLabel()
        label.setItemMode(self.defaultItemMode())
        label.clicked.connect(lambda: self.itemClicked(label))
        label.checked.connect(lambda state: self.itemChecked(state, label))
        return label
    
    def updateWidget(self, widget, data):
        """
        Update the widget
        
        :param QWidget widget: Usually a QwtLegendLabel
        :param QwtLegendData data: Attributes to be displayed

        .. seealso::
        
            :py:meth:`createWidget()`
        
        ... note::
        
            When widget is no QwtLegendLabel updateWidget() does nothing.
        """
        label = widget #TODO: cast to QwtLegendLabel!
        if label is not None:
            label.setData(data)
            if data.value(QwtLegendData.ModeRole) is None:
                label.setItemMode(self.defaultItemMode())
    
    def updateTabOrder(self):
        contentsLayout = self.__data.view.gridLayout
        if contentsLayout is not None:
            w = None
            for i in range(contentsLayout.count()):
                item = contentsLayout.itemAt(i)
                if w is not None and item.widget():
                    QWidget.setTabOrder(w, item.widget())
                w = item.widget()
    
    def sizeHint(self):
        """Return a size hint"""
        hint = self.__data.view.contentsWidget.sizeHint()
        hint += QSize(2*self.frameWidth(), 2*self.frameWidth())
        return hint
        
    def heightForWidth(self, width):
        """
        :param int width: Width
        :return: The preferred height, for a width.
        """
        width -= 2*self.frameWidth()
        h = self.__data.view.contentsWidget.heightForWidth(width)
        if h >= 0:
            h += 2*self.frameWidth()
        return h
    
    def eventFilter(self, object_, event):
        """
        Handle QEvent.ChildRemoved andQEvent.LayoutRequest events 
        for the contentsWidget().

        :param QObject object: Object to be filtered
        :param QEvent event: Event
        :return: Forwarded to QwtAbstractLegend.eventFilter()
        """
        if object_ is self.__data.view.contentsWidget:
            if event.type() == QEvent.ChildRemoved:
                ce = event  #TODO: cast to QChildEvent
                if ce.child().isWidgetType():
                    w = ce.child()  #TODO: cast to QWidget
                    self.__data.itemMap.removeWidget(w)
            elif event.type() == QEvent.LayoutRequest:
                self.__data.view.layoutContents()
                if self.parentWidget() and self.parentWidget().layout() is None:
                    QApplication.postEvent(self.parentWidget(),
                                           QEvent(QEvent.LayoutRequest))
        return QwtAbstractLegend.eventFilter(self, object_, event)
        
    def itemClicked(self, widget):
#        w = self.sender()  #TODO: cast to QWidget
        w = widget
        if w is not None:
            itemInfo = self.__data.itemMap.itemInfo(w)
            if itemInfo is not None:
                widgetList = self.__data.itemMap.legendWidgets(itemInfo)
                if w in widgetList:
                    index = widgetList.index(w)
                    self.clicked.emit(itemInfo, index)
    
    def itemChecked(self, on, widget):
#        w = self.sender()  #TODO: cast to QWidget
        w = widget
        if w is not None:
            itemInfo = self.__data.itemMap.itemInfo(w)
            if itemInfo is not None:
                widgetList = self.__data.itemMap.legendWidgets(itemInfo)
                if w in widgetList:
                    index = widgetList.index(w)
                    self.checked.emit(itemInfo, on, index)
    
    def renderLegend(self, painter, rect, fillBackground):
        """
        Render the legend into a given rectangle.

        :param QPainter painter: Painter
        :param QRectF rect: Bounding rectangle
        :param bool fillBackground: When true, fill rect with the widget background
        """
        if self.__data.itemMap.isEmpty():
            return
        if fillBackground:
            if self.autoFillBackground() or\
               self.testAttribute(Qt.WA_StyledBackground):
                QwtPainter.drawBackground(painter, rect, self)
#    const QwtDynGridLayout *legendLayout = 
#        qobject_cast<QwtDynGridLayout *>( contentsWidget()->layout() );
        #TODO: not the exact same implementation
        legendLayout = self.__data.view.contentsWidget.layout()
        if legendLayout is None:
            return
        left, right, top, bottom = self.getContentsMargins()
        layoutRect = QRect()
        layoutRect.setLeft(np.ceil(rect.left())+left)
        layoutRect.setTop(np.ceil(rect.top())+top)
        layoutRect.setRight(np.ceil(rect.right())-right)
        layoutRect.setBottom(np.ceil(rect.bottom())-bottom)
        numCols = legendLayout.columnsForWidth(layoutRect.width())
        itemRects = legendLayout.layoutItems(layoutRect, numCols)
        index = 0
        for i in range(legendLayout.count()):
            item = legendLayout.itemAt(i)
            w = item.widget()
            if w is not None:
                painter.save()
                painter.setClipRect(itemRects[index], Qt.IntersectClip)
                self.renderItem(painter, w, itemRects[index], fillBackground)
                index += 1
                painter.restore()
                
    def renderItem(self, painter, widget, rect, fillBackground):
        """
        Render a legend entry into a given rectangle.

        :param QPainter painter: Painter
        :param QWidget widget: Widget representing a legend entry
        :param QRectF rect: Bounding rectangle
        :param bool fillBackground: When true, fill rect with the widget background
        """
        if fillBackground:
            if widget.autoFillBackground() or\
               widget.testAttribute(Qt.WA_StyledBackground):
                QwtPainter.drawBackground(painter, rect, widget)
        label = widget  #TODO: cast to QwtLegendLabel
        if label is not None:
            icon = label.data().icon()
            sz = icon.defaultSize()
            iconRect = QRectF(rect.x()+label.margin(),
                              rect.center().y()-.5*sz.height(),
                              sz.width(), sz.height())
            icon.render(painter, iconRect, Qt.KeepAspectRatio)
            titleRect = QRectF(rect)
            titleRect.setX(iconRect.right()+2*label.spacing())
            painter.setFont(label.font())
            painter.setPen(label.palette().color(QPalette.Text))
            label.drawText(painter, titleRect)  #TODO: cast label to QwtLegendLabel
            
    def legendWidgets(self, itemInfo):
        """
        List of widgets associated to a item

        :param QVariant itemInfo: Info about an item
        """
        return self.__data.itemMap.legendWidgets(itemInfo)
    
    def legendWidget(self, itemInfo):
        """
        First widget in the list of widgets associated to an item

        :param QVariant itemInfo: Info about an item
        """
        list_ = self.__data.itemMap.legendWidgets(itemInfo)
        if list_:
            return list_[0]
    
    def itemInfo(self, widget):
        """
        Find the item that is associated to a widget

        :param QWidget widget: Widget on the legend
        :return: Associated item info
        """
        return self.__data.itemMap.itemInfo(widget)
    
    def isEmpty(self):
        return self.__data.itemMap.isEmpty()
