# vPresent is an immersive presentation creation and display application.
# Copyright (C) 2007-2008 by Infiscape Corporation
#
# This program 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.
#
# This program 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 this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

import math
from PyQt4 import QtCore, QtGui

class ArcDial(QtGui.QWidget):
   hourHand = QtGui.QPolygon([
       QtCore.QPoint(7, 8),
       QtCore.QPoint(-7, 8),
       QtCore.QPoint(0, -40)
   ])

   minuteHand = QtGui.QPolygon([
       QtCore.QPoint(7, 8),
       QtCore.QPoint(-7, 8),
       QtCore.QPoint(0, -70)
   ])

   arcColor = QtGui.QColor(0, 0, 250)

   def __init__(self, parent=None):
      QtGui.QWidget.__init__(self, parent)

      self.setMouseTracking(True)
      self.mStartArc = 0.0
      self.mEndArc = 45.0
      self.mGrabbed = 0
      self.mHoverMask = 0
      self.mGrabArc = -1.0

      innerColor = QtGui.QColor(205, 205, 205, 215)
      outerColor = QtGui.QColor(255, 255, 255, 215)
      hoverInnerColor = QtGui.QColor(205, 5, 5, 215)
      hoverOuterColor = QtGui.QColor(255, 55, 55, 215)

      self.bubbleRadius = 5.0
      self.bubbleRadius2 = self.bubbleRadius * self.bubbleRadius
      self.gradient = QtGui.QRadialGradient(QtCore.QPointF(self.bubbleRadius, self.bubbleRadius),
                                            self.bubbleRadius,
                                            QtCore.QPointF(self.bubbleRadius*0.5, self.bubbleRadius*0.5))

      self.gradient.setColorAt(0, QtGui.QColor(255, 255, 255, 255))
      self.gradient.setColorAt(0.25, innerColor)
      self.gradient.setColorAt(1, outerColor)

      self.hoverGradient = QtGui.QRadialGradient(QtCore.QPointF(self.bubbleRadius, self.bubbleRadius),
                                                 self.bubbleRadius,
                                                 QtCore.QPointF(self.bubbleRadius*0.5, self.bubbleRadius*0.5))
      self.hoverGradient.setColorAt(0, QtGui.QColor(255, 255, 255, 255))
      self.hoverGradient.setColorAt(0.25, hoverInnerColor)
      self.hoverGradient.setColorAt(1, hoverOuterColor)

   def setStartArc(self, startArc):
      self.mStartArc = startArc
      self.update()

   def setEndArc(self, endArc):
      self.mEndArc = endArc
      self.update()

   def valueFromPoint(self, p):
      yy = float(self.width())/2.0 - p.x()
      xx = float(p.y()) - self.height()/2.0
      if xx != 0.0 or yy != 0.0:
         a = math.atan2(yy, xx)
      else:
         a = 0.0

      if a < (math.pi / -2.0):
         a = a + math.pi * 2.0

      dist = 0
      minv = 0.0
      maxv = 360.0

      r = maxv - minv
      v =  (0.5 + minv + r * (math.pi * 3 / 2 - a) / (2 * math.pi))
      v = int(v)

      if dist > 0:
         v -= dist

      return v

   def getUnder(self, pnt):
         ct = self.mBoundingRect.center()

         p1 = getPointOnArc(self.mBoundingRect, self.mStartArc)
         p2 = getPointOnArc(self.mBoundingRect, self.mEndArc)

         r = self.mBoundingRect.width()/2.0
         r2 = r*r

         if abs(pnt.x()-p1.x()) < self.bubbleRadius and \
            abs(pnt.y()-p1.y()) < self.bubbleRadius:
            return 1
         elif abs(pnt.x()-p2.x()) < self.bubbleRadius and \
              abs(pnt.y()-p2.y()) < self.bubbleRadius:
            return 2
         else:
            new_value = self.valueFromPoint(pnt)
            d3 = pow(abs(pnt.x()-ct.x()),2) + pow(abs(pnt.y()-ct.y()),2)

            first = False
            if self.mEndArc > self.mStartArc:
               if new_value > min(self.mStartArc, self.mEndArc) and \
                  new_value < max(self.mStartArc, self.mEndArc):
                  first = True
            else:
               if new_value < self.mStartArc or new_value > self.mEndArc:
                  first = True

            if first and d3 > (r2-1000.0) and d3 < (r2+1000.0):
               return 4
         return 0

   def minimumSizeHint(self):
      return QtCore.QSize(50, 50)

   def sizeHint(self):
      return QtCore.QSize(100, 100).expandedTo(QtGui.QApplication.globalStrut())

   def mousePressEvent(self, event):
      event.accept()
      assert self.mGrabbed == 0
      self.mGrabbed = self.getUnder(event.pos())
      if self.mGrabbed & 4:
         self.mGrabArc = self.valueFromPoint(event.pos())

   def mouseReleaseEvent(self, event):
      event.accept()
      self.mGrabbed = 0

   def mouseMoveEvent(self, event):
      #if (!(e->buttons() & Qt::LeftButton)) {
      #  e->ignore();
      #  return;
      #}

      if 0 == self.mGrabbed:
         ng = self.getUnder(event.pos())
         self.mHoverMask = ng
         self.update()
      else:
         new_value = self.valueFromPoint(event.pos())
         if self.mGrabbed & 1:
            self.mStartArc = new_value
            self.update()
         elif self.mGrabbed & 2:
            self.mEndArc = new_value
            self.update()
         elif self.mGrabbed & 4:
            assert self.mGrabArc >= 0.0
            delta = new_value - self.mGrabArc
            self.mStartArc = (self.mStartArc + delta) % 360
            self.mEndArc = (self.mEndArc + delta) % 360
            self.mGrabArc = new_value
            self.update()
         self.emit(QtCore.SIGNAL("valuesChanged"), self.mStartArc, self.mEndArc)


      event.accept()

   def paintControlPoint(self, painter, brush, pnt):
      painter.save()
      painter.translate(pnt.x() - self.bubbleRadius,
                        pnt.y() - self.bubbleRadius)
      painter.setBrush(brush)
      painter.drawEllipse(0, 0, int(2*self.bubbleRadius), int(2*self.bubbleRadius))
      painter.restore()

   def resizeEvent(self, event):
      r = min(self.width(), self.height()) / 2.0
      width = self.width()
      height = self.height()
      d_ = r / 6.0;
      dx = self.rect().x() + d_ + (width - 2 * r) / 2.0 + 1;
      dy = self.rect().y() + d_ + (height - 2 * r) / 2.0 + 1;
      self.mBoundingRect = QtCore.QRectF(int(dx), int(dy), int(r * 2 - 2 * d_ - 2), int(r * 2 - 2 * d_ - 2));

   def paintEvent(self, event):
      painter = QtGui.QPainter(self)
      painter.setRenderHint(QtGui.QPainter.Antialiasing)


      painter.setPen(ArcDial.arcColor)
      painter.setBrush(ArcDial.arcColor)

      if self.mStartArc == self.mEndArc:
         span = 360.0
      elif self.mStartArc > self.mEndArc:
         span = 360.0 - self.mStartArc + self.mEndArc
      else:
         span = self.mEndArc - self.mStartArc

      pen = QtGui.QPen()
      pen.setWidth(1.0)

      if self.mHoverMask & 4:
         pen.setColor(QtGui.QColor(255,0,0))
      else:
         pen.setColor(ArcDial.arcColor)
      painter.setPen(pen)
      painter.drawArc(self.mBoundingRect, self.mStartArc * 16, span * 16);

      if self.mHoverMask & 1:
         brush = QtGui.QBrush(self.hoverGradient)
      else:
         brush = QtGui.QBrush(self.gradient)
      painter.setPen(ArcDial.arcColor)
      self.paintControlPoint(painter, brush, getPointOnArc(self.mBoundingRect, self.mStartArc))

      if self.mHoverMask & 2:
         brush = QtGui.QBrush(self.hoverGradient)
      else:
         brush = QtGui.QBrush(self.gradient)
      painter.setPen(ArcDial.arcColor)
      self.paintControlPoint(painter, brush, getPointOnArc(self.mBoundingRect, self.mEndArc))

      if self.mStartArc > self.mEndArc:
         s = self.mStartArc - 360.0
      else:
         s = self.mStartArc
      span = self.mEndArc - s

      num_textures = 8
      tex_delta = span/num_textures
      for i in xrange(num_textures):
         tp = self.mEndArc - (tex_delta * i)
         painter.drawText(getPointOnArc(self.mBoundingRect, tp), "%s"%i)

def getPointOnArc(rect, angle):
   a = rect.width() / 2.0
   b = rect.height() / 2.0
   return rect.center() + QtCore.QPointF(a * math.cos(math.radians(angle)), -b * math.sin(math.radians(angle)))

if __name__ == "__main__":
   import sys
   app = QtGui.QApplication(sys.argv)
   cs = ArcDial()
   cs.setGeometry(100, 100, 400, 400)
   cs.show()
   sys.exit(app.exec_())
