# -*- coding: utf-8 -*-
"""
formlayout
==========

Module creating Qt form dialogs/layouts to edit various type of parameters


formlayout License Agreement (MIT License)
------------------------------------------

Copyright (c) 2009 Pierre Raybaut

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
"""

from __future__ import print_function

# History:
# 1.0.10: added float validator (disable "Ok" and "Apply" button when not valid)
# 1.0.7: added support for "Apply" button
# 1.0.6: code cleaning

__version__ = '1.0.10'
__license__ = __doc__

DEBUG = False

import sys
STDERR = sys.stderr

from matplotlib.backends.qt4_compat import QtGui,QtCore
from matplotlib.colors import rgb2hex

if not hasattr(QtGui,'QFormLayout'):
    raise ImportError, "Warning: formlayout requires PyQt4 >v4.3 or PySide"

(QWidget, QLineEdit, QComboBox, QLabel, QSpinBox, QIcon,QStyle,
 QDialogButtonBox, QHBoxLayout, QVBoxLayout, QDialog, QColor, QPushButton,
 QCheckBox, QColorDialog, QPixmap, QTabWidget, QApplication, QStackedWidget,
 QDateEdit, QDateTimeEdit, QFont, QFontComboBox, QFontDatabase, QGridLayout,
 QFormLayout, QDoubleValidator) =\
 (QtGui.QWidget, QtGui.QLineEdit, QtGui.QComboBox, QtGui.QLabel,
 QtGui.QSpinBox, QtGui.QIcon, QtGui.QStyle, QtGui.QDialogButtonBox,
 QtGui.QHBoxLayout, QtGui.QVBoxLayout, QtGui.QDialog, QtGui.QColor,
 QtGui.QPushButton, QtGui.QCheckBox, QtGui.QColorDialog, QtGui.QPixmap,
 QtGui.QTabWidget, QtGui.QApplication, QtGui.QStackedWidget, QtGui.QDateEdit,
 QtGui.QDateTimeEdit, QtGui.QFont, QtGui.QFontComboBox, QtGui.QFontDatabase,
 QtGui.QGridLayout, QtGui.QFormLayout, QtGui.QDoubleValidator)

(Qt, SIGNAL, SLOT, QObject, QSize,pyqtSignature, pyqtProperty) =\
(QtCore.Qt, QtCore.SIGNAL, QtCore.SLOT, QtCore.QObject, QtCore.QSize,
 QtCore.Slot, QtCore.Property)

import datetime

class ColorButton(QPushButton):
    """
    Color choosing push button
    """
    __pyqtSignals__ = ("colorChanged(QColor)",)

    def __init__(self, parent=None):
        QPushButton.__init__(self, parent)
        self.setFixedSize(20, 20)
        self.setIconSize(QSize(12, 12))
        self.connect(self, SIGNAL("clicked()"), self.choose_color)
        self._color = QColor()

    def choose_color(self):
        color = QColorDialog.getColor(self._color,self.parentWidget(),'')
        if color.isValid():
            self.set_color(color)

    def get_color(self):
        return self._color

    @QtCore.Slot("QColor")
    def set_color(self, color):
        if color != self._color:
            self._color = color
            self.emit(SIGNAL("colorChanged(QColor)"), self._color)
            pixmap = QPixmap(self.iconSize())
            pixmap.fill(color)
            self.setIcon(QtGui.QIcon(pixmap))

    color = QtCore.Property("QColor", get_color, set_color)

def col2hex(color):
    """Convert matplotlib color to hex before passing to Qt"""
    return rgb2hex(colorConverter.to_rgb(color))

def to_qcolor(color):
    """Create a QColor from a matplotlib color"""
    qcolor = QtGui.QColor()
    color = str(color)
    try:
        color = col2hex(color)
    except ValueError:
        #print('WARNING: ignoring invalid color %r' % color)
        return qcolor # return invalid QColor
    qcolor.setNamedColor(color) # set using hex color
    return qcolor # return valid QColor

def text_to_qcolor(text):
    """
    Create a QColor from specified string
    Avoid warning from Qt when an invalid QColor is instantiated
    """
    color = QColor()
    if isinstance(text, QObject):
        # actually a QString, which is not provided by the new PyQt4 API:
        text = str(text)
    if not isinstance(text, (unicode, str)):
        return color
    if text.startswith('#') and len(text)==7:
        correct = '#0123456789abcdef'
        for char in text:
            if char.lower() not in correct:
                return color
    elif text not in list(QColor.colorNames()):
        return color
    color.setNamedColor(text)
    return color

def is_matplotlib_color(value):
    """
    Check if value is a color passed to us from matplotlib.
    It could either be a valid color string or a 3-tuple of floats between 0. and 1.
    """
    if text_to_qcolor(value).isValid():
        return True
    if isinstance(value,tuple) and len(value)==3 and all(map(lambda v: isinstance(v,float),value)):
        for c in value:
            if c < 0. or c > 1.:
                return False
        return True
    return False

class ColorLayout(QHBoxLayout):
    """Color-specialized QLineEdit layout"""
    def __init__(self, color, parent=None):
        QHBoxLayout.__init__(self)
        assert isinstance(color, QColor)
        self.lineedit = QLineEdit(color.name(), parent)
        self.connect(self.lineedit, SIGNAL("textChanged(QString)"),
                     self.update_color)
        self.addWidget(self.lineedit)
        self.colorbtn = ColorButton(parent)
        self.colorbtn.color = color
        self.connect(self.colorbtn, SIGNAL("colorChanged(QColor)"),
                     self.update_text)
        self.addWidget(self.colorbtn)

    def update_color(self, text):
        color = text_to_qcolor(text)
        if color.isValid():
            self.colorbtn.color = color

    def update_text(self, color):
        self.lineedit.setText(color.name())

    def text(self):
        return self.lineedit.text()


def font_is_installed(font):
    """Check if font is installed"""
    return [fam for fam in QFontDatabase().families() if unicode(fam)==font]

def tuple_to_qfont(tup):
    """
    Create a QFont from tuple:
        (family [string], size [int], italic [bool], bold [bool])
    """
    if not isinstance(tup, tuple) or len(tup) != 4 \
       or not font_is_installed(tup[0]) \
       or not isinstance(tup[1], int) \
       or not isinstance(tup[2], bool) \
       or not isinstance(tup[3], bool):
        return None
    font = QFont()
    family, size, italic, bold = tup
    font.setFamily(family)
    font.setPointSize(size)
    font.setItalic(italic)
    font.setBold(bold)
    return font

def qfont_to_tuple(font):
    return (unicode(font.family()), int(font.pointSize()),
            font.italic(), font.bold())