Rename
authorv13 <v13>
Fri, 26 Feb 2010 11:54:54 +0000 (11:54 +0000)
committerv13 <v13>
Fri, 26 Feb 2010 11:54:54 +0000 (11:54 +0000)
trunk/src/0.py [new file with mode: 0755]
trunk/src/algo.py [new file with mode: 0755]
trunk/src/config.py [new file with mode: 0755]
trunk/src/graph.py [new file with mode: 0755]
trunk/src/main.py [new file with mode: 0755]
trunk/src/win.py [new file with mode: 0755]

diff --git a/trunk/src/0.py b/trunk/src/0.py
new file mode 100755 (executable)
index 0000000..ec78096
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+# coding=UTF-8
+# 
+# Copyright (C) 2010 Stefanos Harhalakis
+#
+# This file is part of mydays.
+#
+# mydays 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 3 of the License, or
+# (at your option) any later version.
+#
+# mydays 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 mydays.  If not, see <http://www.gnu.org/licenses/>.
+#
+# $Id: 0.py 2265 2010-02-21 19:16:26Z v13 $
+
+__version__ = "$Id: 0.py 2265 2010-02-21 19:16:26Z v13 $"
+
+# vim: set ts=8 sts=4 sw=4 noet formatoptions=r ai nocindent:
+
diff --git a/trunk/src/algo.py b/trunk/src/algo.py
new file mode 100755 (executable)
index 0000000..e8f1670
--- /dev/null
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+# coding=UTF-8
+# 
+# Copyright (C) 2010 Stefanos Harhalakis
+#
+# This file is part of mydays.
+#
+# mydays 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 3 of the License, or
+# (at your option) any later version.
+#
+# mydays 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 mydays.  If not, see <http://www.gnu.org/licenses/>.
+#
+# $Id: 0.py 2265 2010-02-21 19:16:26Z v13 $
+
+__version__ = "$Id: 0.py 2265 2010-02-21 19:16:26Z v13 $"
+
+import time
+
+class Algo(object):
+    def __init__(self):
+       t=today()
+       self.setReference(t, 28)
+
+    # set day "day" as the reference day of starting period cycle
+    # day is int(time.time()/86400)
+    # cycle is the cycle width in days, starting from 0
+    def setReference(self, day, cycle):
+       self.refday=day
+       self.cycle=cycle
+
+    # Convert an arbitary day to a day in cycle
+    def dayInCycle(self, day):
+       d=(day-self.refday) % self.cycle
+       if d<0:
+           d+=self.cycle
+
+       return(d)
+
+    def cycleLength(self):
+       return(self.cycle)
+
+    def currentDayInCycle(self):
+       d=today()
+       return(self.dayInCycle(d))
+
+    # Return:
+    # ret={
+    #  'status':   ['ok', 'red']
+    #  'day':      day in status
+    #  'len':      length of status
+    #  }
+    #
+    # e.g.  ret={'status':'red', 'day':0, 'len':5}
+    #      means that it is in red, in the first of 5
+    def status(self, day):
+       d=self.dayInCycle(day)
+
+       ovbefore=14
+
+       if d<5:
+           # Red
+           ret={
+               'status':   'red',
+               'day':      d,
+               'len':      5,
+               }
+       elif d>=self.cycle-6:
+           ret={
+               'status':   'blue',
+               'day':      6+d-self.cycle,
+               'len':      6,
+               }
+       elif d>self.cycle-ovbefore-4 and d<=self.cycle-ovbefore:
+           ret={
+               'status':   'green',
+               'day':      d-self.cycle+ovbefore+3,
+               'len':      4,
+               }
+       elif d>self.cycle-16 and d<=self.cycle-ovbefore+1:
+           ret={
+               'status':   'green',
+               'day':      2,
+               'len':      4,
+               }
+       else:
+           ret={
+               'status':   'ok',
+               'day':      d,
+               'len':      self.cycle
+               }
+
+       return(ret)
+
+def today():
+    t=time.time() + time.timezone
+    day=int(t/86400)
+
+    return(day)
+
+
+# vim: set ts=8 sts=4 sw=4 noet formatoptions=r ai nocindent:
+
diff --git a/trunk/src/config.py b/trunk/src/config.py
new file mode 100755 (executable)
index 0000000..30a3f50
--- /dev/null
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+# coding=UTF-8
+# 
+# Copyright (C) 2010 Stefanos Harhalakis
+#
+# This file is part of mydays.
+#
+# mydays 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 3 of the License, or
+# (at your option) any later version.
+#
+# mydays 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 mydays.  If not, see <http://www.gnu.org/licenses/>.
+#
+# $Id: 0.py 2265 2010-02-21 19:16:26Z v13 $
+
+__version__ = "$Id: 0.py 2265 2010-02-21 19:16:26Z v13 $"
+
+import pickle
+import time
+import algo
+
+fn="/home/user/.maedays"
+
+defaults={
+    'cycle':   28,
+    'day0':    algo.today(),
+    }
+
+def store(name, cycle, day0):
+    # Load old
+    all=loadAll()
+    # Override
+    all[name]={
+       'cycle':    cycle,
+       'day0':     day0,
+       }
+
+    st=pickle.dumps(all)
+    f=file(fn, "w")
+    f.write(st)
+    f.close()
+
+def loadOne(name):
+    all=loadAll()
+    if all.has_key(name):
+       ret=all[name]
+    else:
+       ret=defaults
+
+    return(ret)
+
+def loadAll():
+    try:
+       f=file(fn, "r")
+       st=f.read()
+       f.close()
+       ret=pickle.loads(st)
+    except:
+       ret={}
+
+    return(ret)
+
+# vim: set ts=8 sts=4 sw=4 noet formatoptions=r ai nocindent:
+
diff --git a/trunk/src/graph.py b/trunk/src/graph.py
new file mode 100755 (executable)
index 0000000..9d34648
--- /dev/null
@@ -0,0 +1,262 @@
+#!/usr/bin/env python
+# coding=UTF-8
+# 
+# Copyright (C) 2010 Stefanos Harhalakis
+#
+# This file is part of mydays.
+#
+# mydays 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 3 of the License, or
+# (at your option) any later version.
+#
+# mydays 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 mydays.  If not, see <http://www.gnu.org/licenses/>.
+#
+# $Id: 0.py 2265 2010-02-21 19:16:26Z v13 $
+
+__version__ = "$Id: 0.py 2265 2010-02-21 19:16:26Z v13 $"
+
+from PyQt4.QtGui import *
+from PyQt4.QtCore import *
+
+import time
+
+import algo
+
+class DaysGraph(QWidget):
+    def __init__(self, algo_, *args, **kwargs):
+       QWidget.__init__(self, *args, **kwargs)
+
+       self.min_day_width=20
+       self.max_day_width=50
+       self.day_width=50
+       self.algo=algo_
+
+       self.first_day=algo.today()
+
+       self.last_x=None
+       self.last_y=None
+
+    def setAlgo(self, algo_):
+       self.algo=algo_
+
+    def reset(self):
+       self.first_day=algo.today()
+       self.repaint()
+
+    def paintEvent(self, event):
+       w0=self.width()
+       h=self.height()
+
+       gray=0x22
+
+       days=int(w0/self.day_width)
+       days=int((days)/2)*2 + 3
+       w=days*self.day_width
+
+       # Now calculate the x0
+       x0=int((w0-w)/2)
+
+       painter=QPainter(self)
+
+       # This makes it sloooooooooooow
+#      painter.setRenderHints(painter.Antialiasing \
+#          | painter.TextAntialiasing \
+#          | painter.HighQualityAntialiasing
+#          )
+
+       # common day outline
+       colol=QColor(0x66,0x66,0x66)
+       penol=QPen(colol)
+
+       # outline of the small block 
+       colblockol=Qt.white
+       penblockol=QPen(colblockol)
+
+       # Current day outline
+       colcur=Qt.white
+       pencur=QPen(colcur, 3)
+
+       # Text color
+       coltxt=Qt.white
+       pentxt=QPen(coltxt)
+
+       # Font
+       fontdate=QFont()
+       fontdate.setPointSizeF(fontdate.pointSizeF()*0.5)
+       fontdate_m=QFontMetrics(fontdate)
+
+       # Last bounding rectagle of the text
+       last_br=QRect(0,0,4,4)
+
+       today=algo.today()
+
+       for d in xrange(days):
+           # This will cause the first_day to be at the center
+           curday=self.first_day+d - int(days/2)
+           x=x0+d*self.day_width
+           y=0
+
+           h2=h-50
+
+           st=self.algo.status(curday)
+
+           if st['status']=='ok':
+               colbg=QColor(gray, gray, gray)
+               brbg=QBrush(colbg)
+
+               painter.setPen(penol)
+               painter.fillRect(x, y, self.day_width-1, h2-1, brbg) 
+           elif st['status']=='red' or \
+               st['status']=='blue' or \
+               st['status']=='green':
+               if st['status']=='red':
+                   perc=1.0*st['day'] / (st['len'])
+                   colbg=QColor(
+                       0xff - int((0xff - gray - gray) * perc),
+                       int(gray * perc),
+                       int(gray * perc))
+                   colblock=Qt.red
+               elif st['status']=='blue':
+                   perc=1.0*(st['day']+1) / (st['len'])
+                   colbg=QColor(
+                       gray,
+                       gray,
+                       gray + ((0xff-gray) * perc))
+                   colblock=Qt.blue
+               elif st['status']=='green':
+                   perc=1.0*(st['day']+1) / (st['len'])
+                   colbg=QColor(
+                       gray,
+                       gray + ((0xff-gray) * perc),
+                       gray)
+                   colblock=Qt.green
+               brbg=QBrush(colbg)
+               painter.fillRect(x, y, self.day_width-1, h2-1, brbg) 
+
+               # Show a nice small box for interesting days
+               boxw=20
+               boxh=10
+
+               # Ensure that boxw is sane - e.g. not larger than
+               # the current day_width
+               boxw2=self.day_width-10
+               boxw=min(boxw, boxw2)
+
+               x1=x+int((self.day_width-boxw)/2)
+
+               if st['status']=='red':
+                   y1=y + int(perc * h2*2/3) + 20
+               elif st['status']=='blue' or st['status']=='green':
+                   y1=y + int(h2*2/3) - int(perc * h2*2/3) + 20
+
+               brbg=QBrush(colblock)
+               painter.fillRect(x1, y1, boxw, boxh, brbg)
+               painter.setPen(colblockol)
+               painter.drawRect(x1, y1, boxw, boxh)
+
+           painter.setPen(colol)
+           painter.drawRect(x, y, self.day_width, h2-1)
+
+           # Highlight today
+           if curday==today:
+               painter.setPen(pencur)
+               painter.drawRect(x+2,y+2,self.day_width-4, h2-5)
+
+           date=time.localtime(curday*86400+30000)
+
+           y2=h2
+
+           txt="%d.%02d\n%d" % (date.tm_mday, date.tm_mon, date.tm_year)
+
+           # br is used to determine overlaps
+           br=fontdate_m.boundingRect(txt)
+           br.translate(x,y2)
+
+           colnum=1
+           while colnum*self.day_width < br.width():
+               colnum+=2
+
+           # If this intersects with the previous day then it is ok
+           if br.intersects(last_br):
+               continue
+           last_br=br
+
+           # br2 is the true bounding rectangle
+           # The width calculation helps with alignment
+           br2=QRect(x-((colnum-1)/2 * self.day_width), y2,
+               self.day_width*colnum, fontdate_m.height()*2)
+
+           # Draw it
+           painter.setFont(fontdate)
+           painter.setPen(pentxt)
+           painter.drawText(br2, Qt.AlignCenter | Qt.AlignTop, txt)
+
+    # Handle both horizontal and vertical movement
+    def mouseMoveEvent(self, event):
+       pos=event.pos()
+       x=pos.x()
+       y=pos.y()
+
+       torepaint=False
+
+       if self.last_x==None:
+           self.last_x=x
+           self.last_y=y
+
+       # Vertical movement - that much pixels account for one zoom
+       y_step=50
+
+       # First look horizontal movement
+       # If there was movement the reset vertical movement
+       dx=x-self.last_x
+       if dx>0:
+           days=int(dx/self.day_width)
+       else:
+           days=-int(abs(dx)/self.day_width)
+
+       if days!=0:
+           self.first_day-=days
+           self.last_x+=(days*self.day_width)
+           self.last_y=y       # Reset vertical movement
+           torepaint=True
+
+       # Now look for vertical movement
+       dy=y-self.last_y
+       if dy>0:
+           zoom=int(dy/y_step)
+       else:
+           zoom=-int(abs(dy)/y_step)
+
+       if zoom!=0:
+           t=self.day_width+zoom
+           if t<self.min_day_width:
+               t=self.min_day_width
+           elif t>self.max_day_width:
+               t=self.max_day_width
+           #self.day_width-=zoom
+           self.day_width=t
+
+           self.last_y+=zoom*y_step
+           self.last_x=x       # Reset horizontal movement
+           torepaint=True
+       
+       if torepaint:
+           self.repaint()
+
+    def mousePressEvent(self, event):
+       pos=event.pos()
+       self.last_x=pos.x()
+       self.last_y=pos.y()
+
+    def sizeHint(self):
+       return(QSize(100,50))
+
+# vim: set ts=8 sts=4 sw=4 noet formatoptions=r ai nocindent:
+
diff --git a/trunk/src/main.py b/trunk/src/main.py
new file mode 100755 (executable)
index 0000000..9e0b50f
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+# coding=UTF-8
+# 
+# Copyright (C) 2010 Stefanos Harhalakis
+#
+# This file is part of mydays.
+#
+# mydays 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 3 of the License, or
+# (at your option) any later version.
+#
+# mydays 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 mydays.  If not, see <http://www.gnu.org/licenses/>.
+#
+# $Id: 0.py 2265 2010-02-21 19:16:26Z v13 $
+
+__version__ = "$Id: 0.py 2265 2010-02-21 19:16:26Z v13 $"
+
+import win
+import algo
+import config
+
+def init():
+    global win, al
+
+    al=algo.Algo()
+    win.init(al)
+
+    dt=config.loadOne("default")
+    al.setReference(dt['day0'], dt['cycle'])
+
+init()
+
+win.doit()
+
+# vim: set ts=8 sts=4 sw=4 noet formatoptions=r ai nocindent:
+
diff --git a/trunk/src/win.py b/trunk/src/win.py
new file mode 100755 (executable)
index 0000000..17dbca0
--- /dev/null
@@ -0,0 +1,358 @@
+#!/usr/bin/env python
+# coding=UTF-8
+# 
+# Copyright (C) 2010 Stefanos Harhalakis
+#
+# This file is part of mydays.
+#
+# mydays 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 3 of the License, or
+# (at your option) any later version.
+#
+# mydays 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 mydays.  If not, see <http://www.gnu.org/licenses/>.
+#
+# $Id: 0.py 2265 2010-02-21 19:16:26Z v13 $
+
+__version__ = "$Id: 0.py 2265 2010-02-21 19:16:26Z v13 $"
+
+from PyQt4.QtGui import *
+from PyQt4.QtCore import *
+
+import sys
+import time
+
+from graph import DaysGraph
+import config
+import algo
+
+app=None
+win=None
+
+class ConfigDialog(QDialog):
+    def __init__(self, *args, **kwargs):
+       QDialog.__init__(self, *args, **kwargs)
+
+       self.editCycle=QSpinBox(self)
+       self.editCurrent=QSpinBox(self)
+       self.editCycle.setRange(10,50)
+       self.editCurrent.setRange(1,50)
+       self.editCurrent.setWrapping(True)
+       self.editCycle.setSuffix(" days")
+
+       self.editCycle.valueChanged.connect(self.slotEditCycleChanged)
+
+       self.l0=QHBoxLayout(self)
+
+       l1=QFormLayout()
+       l1.addRow("Cycle length:", self.editCycle)
+       l1.addRow("Current day in cycle:", self.editCurrent)
+
+       self.l0.addLayout(l1)
+
+       spacer=QSpacerItem(20, 20, QSizePolicy.Expanding)
+       self.l0.addItem(spacer)
+
+       l2=QVBoxLayout()
+       self.l0.addLayout(l2)
+
+       self.buttonOk=QPushButton(self)
+       self.buttonOk.setText("OK")
+       self.buttonOk.clicked.connect(self.slotButOk)
+       l2.addWidget(self.buttonOk)
+
+       spacer=QSpacerItem(20, 20, QSizePolicy.Minimum,QSizePolicy.Expanding)
+       l2.addItem(spacer)
+
+       self.setWindowTitle("Configuration")
+
+    def slotButOk(self):
+       self.cycle=self.editCycle.value()
+       self.current=self.editCurrent.value()-1
+
+       self.accept()
+
+    def slotEditCycleChanged(self, value):
+       self.editCurrent.setMaximum(value)
+
+    # current starts from 0
+    def initValues(self, cycle, current):
+       self.cycle=cycle
+       self.current=current
+       self.editCycle.setValue(cycle)
+       self.editCurrent.setValue(current+1)
+
+class MyMsgDialog(QDialog):
+    """
+    A Dialog to show a finger-scrollable message
+
+    Typical usage:
+
+    class Koko(MyMsgDialog):
+       def __init__(....)
+           MyMsgDialog.__init__(....)
+
+
+           self.setWindowTitle("My title")
+    
+           l1=QLabel("koko", self.w)
+           self.l.addWidget(l1)
+           ...
+
+           self.l is a QVBoxLayout. Add everything there.
+           self.w is a QWidget. Use it as parent.
+
+    """
+    def __init__(self, *args, **kwargs):
+       QDialog.__init__(self, *args, **kwargs)
+
+       # This freaking thing is hard
+       # It needs two layouts, one extra widget, the fingerscrollable
+       # property set to true *and* setWidgetResizable(True)
+       self._mm_l0=QVBoxLayout(self)
+
+       self._mm_q=QScrollArea(self)
+       self._mm_q.setWidgetResizable(True)
+       self._mm_q.setProperty('FingerScrollable', True)
+
+       self.w=QWidget(self._mm_q)
+
+       self.l=QVBoxLayout(self.w)
+       self._mm_q.setWidget(self.w)
+       self._mm_l0.addWidget(self._mm_q)
+
+class AboutDialog2(QDialog):
+    def __init__(self, *args, **kwargs):
+       QDialog.__init__(self, *args, **kwargs)
+
+       txt="""
+<p> A program to monitor the women's cycle.  Good for planning (or acting ;-).
+Inspired by "MyDays" app which is (was?) available for Java ME capable phones.
+
+<p> WARNING!!! This is not accurate nor correct! You cannot trust
+this program (or any other program) for accurate predictions!
+(after all, this is about women... how can one be sure :-).
+
+<p> Copyright &copy; 2010, Stefanos Harhalakis &lt;v13@v13.gr&gt;
+
+<p> Send comments and bug reports to the above address.
+
+<p> This program can be distributed under the terms of the GNU public
+license, version 3 or any later.
+       """
+
+       self.setWindowTitle("About MaeDays")
+
+       # This freaking thing is hard
+       # It needs two layouts, one extra widget, the fingerscrollable
+       # property set to true *and* setWidgetResizable(True)
+       self.l0=QVBoxLayout(self)
+
+       self.q=QScrollArea(self)
+       self.q.setWidgetResizable(True)
+       self.q.setProperty('FingerScrollable', True)
+       self.w1=QWidget(self.q)
+
+       self.l=QVBoxLayout(self.w1)
+
+       self.ltitle=QLabel("MaeDays", self.w1)
+       self.ltitle.setObjectName("title")
+       self.ltitle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
+       self.ltitle.setAlignment(Qt.AlignCenter)
+       self.l.addWidget(self.ltitle)
+
+       self.label=QLabel(txt, self.w1)
+       self.label.setWordWrap(True)
+       self.label.setTextFormat(Qt.RichText)
+       self.label.setAlignment(Qt.AlignJustify)
+
+       self.l.addWidget(self.label)
+       self.q.setWidget(self.w1)
+       self.l0.addWidget(self.q)
+
+       self.ltitle.setStyleSheet("""
+       QLabel {
+           font-size:      25pt;
+           color:          rgb(192,192,192);
+           margin-bottom:  0.5ex;
+           }
+       """)
+
+class AboutDialog(MyMsgDialog):
+    def __init__(self, *args, **kwargs):
+       MyMsgDialog.__init__(self, *args, **kwargs)
+
+       txt="""
+<p> A program to monitor the women's cycle.  Good for planning (or acting ;-).
+Inspired by "MyDays" app which is (was?) available for Java ME capable phones.
+
+<p style="color: orange;">
+WARNING!!! This is not accurate nor correct! You cannot trust
+this program (or any other program) for accurate predictions!
+(after all, this is about women... how can one be sure :-).
+
+<p> Copyright &copy; 2010, Stefanos Harhalakis &lt;v13@v13.gr&gt;
+
+<p> Send comments and bug reports to the above address.
+
+<p> This program can be distributed under the terms of the GNU public
+license, version 3 or any later.
+       """
+
+       self.setWindowTitle("About MaeDays")
+
+       self.ltitle=QLabel("MaeDays", self.w)
+       self.ltitle.setObjectName("title")
+       self.ltitle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
+       self.ltitle.setAlignment(Qt.AlignCenter)
+       self.l.addWidget(self.ltitle)
+
+       self.label=QLabel(txt, self.w)
+       self.label.setWordWrap(True)
+       self.label.setTextFormat(Qt.RichText)
+       self.label.setAlignment(Qt.AlignJustify)
+       self.l.addWidget(self.label)
+
+       self.ltitle.setStyleSheet("""
+       QLabel {
+           font-size:      25pt;
+           color:          rgb(192,192,192);
+           margin-bottom:  0.5ex;
+           }
+       """)
+
+class HelpDialog(MyMsgDialog):
+    def __init__(self, *args, **kwargs):
+       MyMsgDialog.__init__(self, *args, **kwargs)
+
+       txt="""
+<p> MaeDays shows information about women's cycle using some generic
+guidelines.  It assumes that the ovolution happens 14 days before the start
+of the next period and that the period cycle is constant. Also, it assumes
+that sperm can live for 4 days, while an egg can live for 2 days.
+
+<p style="color: orange;">
+WARNING!!! This is not always correct. There are FAR TOO MANY exceptions
+to the above rules!!!
+
+<p> Assuming that you understand the risk of being wrong, you become
+entitle to read the graph as follows:
+<p> <span style="color: red">In red:</span> The days that menstruation
+happens.
+<p> <span style="color: green">In green:</span> The fertile days.
+<p> <span style="color: blue">In blue:</span> The days of PMS
+(Premenstrual Syndrome)
+
+<p> Navigation is easy: Use left-right finger movement to move the calendar
+view. Use up-down finger movement to zoom in/out.
+       """
+
+       self.setWindowTitle("Help")
+
+       self.label=QLabel(txt, self.w)
+       self.label.setWordWrap(True)
+       self.label.setTextFormat(Qt.RichText)
+       self.label.setAlignment(Qt.AlignJustify)
+       self.l.addWidget(self.label)
+
+class MyDays(QMainWindow):
+    def __init__(self, algo):
+       QMainWindow.__init__(self)
+
+       self.setupUi(algo)
+
+       self.dlgConfig=ConfigDialog(self)
+       self.dlgAbout=AboutDialog(self)
+       self.dlgHelp=HelpDialog(self)
+
+       self.algo=algo
+
+    def setupUi(self, algo):
+       self.centralwidget=QWidget(self)
+       self.setCentralWidget(self.centralwidget)
+
+       self.l0=QVBoxLayout(self.centralwidget)
+
+       self.dg=DaysGraph(algo, self.centralwidget)
+       self.l0.addWidget(self.dg)
+
+       # Menu
+       self.menuconfig=QAction('Configure', self)
+       self.menuconfig.triggered.connect(self.menuConfig)
+
+       self.menureset=QAction('Go to today', self)
+       self.menureset.triggered.connect(self.menuReset)
+
+       self.menuabout=QAction('About', self)
+       self.menuabout.triggered.connect(self.menuAbout)
+
+       self.menuhelp=QAction('Help', self)
+       self.menuhelp.triggered.connect(self.menuHelp)
+
+       m=self.menuBar()
+       m.addAction(self.menuconfig)
+       m.addAction(self.menureset)
+       m.addAction(self.menuabout)
+       m.addAction(self.menuhelp)
+
+       self.setWindowTitle("MaeDays")
+
+    def setAlgo(self, algo):
+       self.dg.setAlgo(algo)
+
+    def menuConfig(self):
+       if self.dlgConfig==None:
+           self.dlgConfig=ConfigDialog(self)
+
+       self.dlgConfig.initValues(self.algo.cycleLength(),
+           self.algo.currentDayInCycle())
+
+       ret=self.dlgConfig.exec_()
+
+       if ret==self.dlgConfig.Accepted:
+           today=algo.today()
+           day0=today-self.dlgConfig.current
+           self.algo.setReference(day0, self.dlgConfig.cycle)
+           config.store("default", self.dlgConfig.cycle, day0)
+           self.repaint()
+
+    def menuAbout(self):
+       if self.dlgAbout==None:
+           self.dlgAbout=AboutDialog(self)
+
+       ret=self.dlgAbout.exec_()
+
+    def menuHelp(self):
+       if self.dlgHelp==None:
+           self.dlgHelp=HelpDialog(self)
+
+       ret=self.dlgHelp.exec_()
+
+    def menuReset(self):
+       self.dg.reset()
+
+def init(algo):
+    global app
+    global win
+
+    app=QApplication(sys.argv)
+    #app.setAttribute(Qt.WA_Maemo5PortraitOrientation, True);
+    win=MyDays(algo)
+    win.show()
+
+def setAlgo(algo):
+    global win
+    win.setAlgo(algo)
+
+def doit():
+    global app
+    app.exec_()
+
+# vim: set ts=8 sts=4 sw=4 noet formatoptions=r ai nocindent:
+