Added code base.
authorVisa Putkinen <visa.putkinen@iki.fi>
Sat, 26 Feb 2011 22:21:07 +0000 (00:21 +0200)
committerVisa Putkinen <visa.putkinen@iki.fi>
Sat, 26 Feb 2011 22:21:07 +0000 (00:21 +0200)
36 files changed:
weightgraph/changelog.sh [new file with mode: 0755]
weightgraph/debian/changelog [new file with mode: 0644]
weightgraph/debian/compat [new file with mode: 0644]
weightgraph/debian/control [new file with mode: 0644]
weightgraph/debian/copyright [new file with mode: 0644]
weightgraph/debian/optify [new file with mode: 0644]
weightgraph/debian/postinst.ex [new file with mode: 0644]
weightgraph/debian/rules [new file with mode: 0755]
weightgraph/editwindow.cpp [new file with mode: 0644]
weightgraph/editwindow.h [new file with mode: 0644]
weightgraph/img/scale.xcf [new file with mode: 0644]
weightgraph/maemofiles/48/weightgraph.png [new file with mode: 0644]
weightgraph/maemofiles/64/weightgraph.png [new file with mode: 0644]
weightgraph/maemofiles/weightgraph.desktop [new file with mode: 0644]
weightgraph/main.cpp [new file with mode: 0644]
weightgraph/mainwindow.cpp [new file with mode: 0644]
weightgraph/mainwindow.h [new file with mode: 0644]
weightgraph/mainwindow.ui [new file with mode: 0644]
weightgraph/makepackage.sh [new file with mode: 0755]
weightgraph/res.qrc [new file with mode: 0644]
weightgraph/settings.cpp [new file with mode: 0644]
weightgraph/settings.h [new file with mode: 0644]
weightgraph/settingswindow.cpp [new file with mode: 0644]
weightgraph/settingswindow.h [new file with mode: 0644]
weightgraph/weightdata.cpp [new file with mode: 0644]
weightgraph/weightdata.h [new file with mode: 0644]
weightgraph/weightgraph.pro [new file with mode: 0644]
weightgraph/weightgraph.pro.user [new file with mode: 0644]
weightgraph/weightgraphview.cpp [new file with mode: 0644]
weightgraph/weightgraphview.h [new file with mode: 0644]
weightgraph/weightspinbox.h [new file with mode: 0644]
weightgraph/weightstatsview.cpp [new file with mode: 0644]
weightgraph/weightstatsview.h [new file with mode: 0644]
weightgraph/weightview.h [new file with mode: 0644]
welcome [deleted file]
www/index.html

diff --git a/weightgraph/changelog.sh b/weightgraph/changelog.sh
new file mode 100755 (executable)
index 0000000..9c963d0
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+dch --no-auto-nmu -i -m
diff --git a/weightgraph/debian/changelog b/weightgraph/debian/changelog
new file mode 100644 (file)
index 0000000..af52f7c
--- /dev/null
@@ -0,0 +1,5 @@
+weightgraph (1.0.0) unstable; urgency=low
+
+  * Initial Release.
+
+ -- Visa Putkinen <visa.putkinen@iki.fi>  Mon, 21 Feb 2011 20:21:04 +0200
diff --git a/weightgraph/debian/compat b/weightgraph/debian/compat
new file mode 100644 (file)
index 0000000..7ed6ff8
--- /dev/null
@@ -0,0 +1 @@
+5
diff --git a/weightgraph/debian/control b/weightgraph/debian/control
new file mode 100644 (file)
index 0000000..8536dd5
--- /dev/null
@@ -0,0 +1,121 @@
+Source: weightgraph
+Section: user/utilities
+Priority: optional
+Maintainer: Visa Putkinen <visa.putkinen@iki.fi>
+Build-Depends: debhelper (>= 5), libqt4-dev
+Standards-Version: 3.7.3
+Homepage: <insert the upstream URL, if relevant>
+
+Package: weightgraph
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: Keep track of your weight.
+ Use weightgraph to record your daily weights. It will plot a nice graph of the
+ weights to help you keep up your weight control motivation.
+XSBC-Bugtracker: mailto:visa.putkinen@iki.fi
+XSBC-Maemo-Display-Name: WeightGraph
+XB-Maemo-Icon-26:
+ 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
diff --git a/weightgraph/debian/copyright b/weightgraph/debian/copyright
new file mode 100644 (file)
index 0000000..d1237f0
--- /dev/null
@@ -0,0 +1,29 @@
+Upstream Author(s):
+
+    Visa Putkinen <visa.putkinen@iki.fi>
+
+Copyright:
+
+    <Copyright (C) 2011 Visa Putkinen>
+
+License:
+
+    This package 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 package 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 package; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+
+On Debian systems, the complete text of the GNU General
+Public License can be found in `/usr/share/common-licenses/GPL'.
+
+The Debian packaging is (C) 2011, Visa Putkinen <visa@nana> and
+is licensed under the GPL, see above.
diff --git a/weightgraph/debian/optify b/weightgraph/debian/optify
new file mode 100644 (file)
index 0000000..865faf1
--- /dev/null
@@ -0,0 +1 @@
+auto
diff --git a/weightgraph/debian/postinst.ex b/weightgraph/debian/postinst.ex
new file mode 100644 (file)
index 0000000..fd8811d
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+# postinst script for weightgraph
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <postinst> `configure' <most-recently-configured-version>
+#        * <old-postinst> `abort-upgrade' <new version>
+#        * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+#          <new-version>
+#        * <postinst> `abort-remove'
+#        * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+#          <failed-install-package> <version> `removing'
+#          <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+    configure)
+      gtk-update-icon-cache -f /usr/share/icons/hicolor
+    ;;
+
+    abort-upgrade|abort-remove|abort-deconfigure)
+    ;;
+
+    *)
+        echo "postinst called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/weightgraph/debian/rules b/weightgraph/debian/rules
new file mode 100755 (executable)
index 0000000..d466bcd
--- /dev/null
@@ -0,0 +1,60 @@
+#!/usr/bin/make -f
+APPNAME = weightgraph
+
+# Use hardware floating point
+ifneq (,$(findstring vfp,$(DEB_BUILD_OPTIONS)))
+CFLAGS += -mfpu=vfp -mfloat-abi=softfp
+endif
+
+builddir:
+       mkdir -p builddir
+
+builddir/Makefile: builddir
+       cd builddir && qmake-qt4 PREFIX=/usr ../$(APPNAME).pro
+
+build: build-stamp
+
+build-stamp: builddir/Makefile
+       dh_testdir
+       # compile commands go here
+       cd builddir && $(MAKE) -j4
+       touch $@
+
+clean:
+       dh_testdir
+       dh_testroot
+       rm -f build-stamp
+       # cleanup commands go here
+       rm -rf builddir
+       dh_clean
+install: build
+       dh_testdir
+       dh_testroot
+       dh_clean -k
+       dh_installdirs
+       # install commands go here
+       cd builddir && $(MAKE) INSTALL_ROOT=$(CURDIR)/debian/$(APPNAME) install
+
+# Build arch-independent files here
+binary-indep: build install
+#We have nothing to do by default
+
+# Build architecture-dependent files here.
+binary-arch: build install
+       dh_testdir
+       dh_testroot
+       dh_installdocs
+       dh_installexamples
+       dh_installman
+       dh_link
+#      dh_strip --dbg-package=appname-dbg
+       dh_compress
+       dh_fixperms
+       dh_installdeb
+       dh_shlibdeps
+       dh_gencontrol
+       dh_md5sums
+       dh_builddeb
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install configure
diff --git a/weightgraph/editwindow.cpp b/weightgraph/editwindow.cpp
new file mode 100644 (file)
index 0000000..1d54ac0
--- /dev/null
@@ -0,0 +1,149 @@
+#include "editwindow.h"
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+#include <QMessageBox>
+#include <QLabel>
+#ifdef Q_WS_MAEMO_5
+#include <QMaemo5DatePickSelector>
+#endif
+extern WeightDataModel *wdm;
+
+EditWindow::EditWindow(QWidget *parent) :
+    QMainWindow(parent)
+{
+#ifdef Q_WS_MAEMO_5
+  setAttribute(Qt::WA_Maemo5StackedWindow);
+#endif
+
+  setWindowTitle("WeightGraph");
+
+  QWidget *centralContainer = new QWidget(parent);
+  QHBoxLayout *horiz = new QHBoxLayout(centralContainer);
+
+  weightView.setModel(wdm);
+  weightView.setMinimumWidth(540);
+  horiz->addWidget(&weightView);
+
+  QWidget *rightContainer = new QWidget(centralContainer);
+  QVBoxLayout *right = new QVBoxLayout(rightContainer);
+
+  addButton.setText("Add");
+  addButton.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+  connect(&addButton, SIGNAL(clicked()), this, SLOT(addWeight()));
+  right->addWidget(&addButton);
+
+  removeButton.setText("Remove");
+  removeButton.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+  connect(&removeButton, SIGNAL(clicked()), this, SLOT(removeSelected()));
+  right->addWidget(&removeButton);
+
+  editButton.setText("Edit");
+  editButton.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+  connect(&editButton, SIGNAL(clicked()), this, SLOT(editSelected()));
+  right->addWidget(&editButton);
+
+  horiz->addWidget(rightContainer);
+
+  setCentralWidget(centralContainer);
+
+  connect(weightView.selectionModel(),
+          SIGNAL(selectionChanged(const QItemSelection &,const QItemSelection &)),
+          this, SLOT(updateButtons()));
+  updateButtons();
+
+}
+AddWeightDialog::AddWeightDialog(QWidget *parent)
+    : QDialog(parent)
+{
+  setModal(true);
+  QGridLayout *layout = new QGridLayout(this);
+
+  QLabel *dateLabel = new QLabel("Date:", this);
+  //dateLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Maximum);
+  layout->addWidget(dateLabel, 0, 0);
+
+#ifdef Q_WS_MAEMO_5
+  date = new QMaemo5ValueButton(this);
+  QMaemo5DatePickSelector *picker = new QMaemo5DatePickSelector(this);
+  picker->setCurrentDate(QDate::currentDate());
+  date->setPickSelector(picker);
+#else
+  date = new QDateEdit(this);
+  date->setDate(QDate::currentDate());
+  date->setDisplayFormat("yyyy-MM-dd");
+#endif
+  layout->addWidget(date, 1, 0);
+
+  QLabel *weightLabel = new QLabel("Weight:", this);
+  //weightLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Maximum);
+  layout->addWidget(weightLabel, 0, 1);
+
+  weight = new WeightSpinBox(this);
+  layout->addWidget(weight, 1, 1);
+
+  QPushButton *cancelButton = new QPushButton("Cancel", this);
+  connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
+  layout->addWidget(cancelButton, 2, 0);
+
+  QPushButton *addButton = new QPushButton("Add", this);
+  connect(addButton, SIGNAL(clicked()), this, SLOT(accept()));
+  layout->addWidget(addButton, 2, 1);
+}
+WeightDataModel::DateWeight AddWeightDialog::getDateWeight()
+{
+  WeightDataModel::DateWeight ret;
+#ifdef Q_WS_MAEMO_5
+  ret.date = dynamic_cast<QMaemo5DatePickSelector*>(date->pickSelector())->currentDate();
+#else
+  ret.date = date->date();
+#endif
+  ret.weight = weight->value();
+  return ret;
+}
+
+void EditWindow::updateButtons()
+{
+  if (weightView.selectionModel()->selection().indexes().size() == 1) {
+    removeButton.setEnabled(true);
+    editButton.setEnabled(true);
+  }
+  else {
+    removeButton.setEnabled(false);
+    editButton.setEnabled(false);
+  }
+}
+
+extern WeightDataModel *wdm;
+void EditWindow::addWeight()
+{
+  AddWeightDialog d;
+  int code = d.exec();
+  if (code == QDialog::Accepted) {
+    WeightDataModel::DateWeight dw = d.getDateWeight();
+    if (wdm->dateExists(dw.date)) {
+      QMessageBox::warning(this, "Duplicate", "A weight for this date already exists.\n"
+                                              "Only one weight per date allowed.");
+      return;
+    }
+    wdm->setWeightForDate(dw);
+  }
+}
+
+void EditWindow::removeSelected()
+{
+  int answer =
+      QMessageBox::question(this, "Remove?", "Really remove selected entry?",
+                            QMessageBox::No, QMessageBox::Yes);
+  if (answer == QMessageBox::Yes) {
+    QModelIndexList indexes = weightView.selectionModel()->selection().indexes();
+    foreach(QModelIndex i, indexes)
+      wdm->removeRows(i.row(), 1);
+  }
+}
+
+void EditWindow::editSelected()
+{
+  QModelIndexList indexes = weightView.selectionModel()->selection().indexes();
+  if (indexes.size() == 1)
+    weightView.edit(wdm->index(indexes.first().row(), 1));
+}
diff --git a/weightgraph/editwindow.h b/weightgraph/editwindow.h
new file mode 100644 (file)
index 0000000..6a162c2
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef EDITWINDOW_H
+#define EDITWINDOW_H
+
+#include <QMainWindow>
+#include <QPushButton>
+#include <QDateEdit>
+#ifdef Q_WS_MAEMO_5
+#include <QMaemo5ValueButton>
+#endif
+#include <QGridLayout>
+#include <QDialog>
+#include "weightview.h"
+#include "weightdata.h"
+
+class EditWindow : public QMainWindow
+{
+  Q_OBJECT
+public:
+  explicit EditWindow(QWidget *parent = 0);
+public slots:
+  void updateButtons();
+  void addWeight();
+  void removeSelected();
+  void editSelected();
+private:
+  WeightView weightView;
+  QPushButton addButton, removeButton, editButton;
+};
+
+class AddWeightDialog : public QDialog
+{
+  Q_OBJECT
+public:
+  AddWeightDialog(QWidget *parent=0);
+  WeightDataModel::DateWeight getDateWeight();
+
+private:
+#ifdef Q_WS_MAEMO_5
+  QMaemo5ValueButton *date;
+#else
+  QDateEdit *date;
+#endif
+  WeightSpinBox *weight;
+};
+
+
+#endif // EDITWINDOW_H
diff --git a/weightgraph/img/scale.xcf b/weightgraph/img/scale.xcf
new file mode 100644 (file)
index 0000000..53932fa
Binary files /dev/null and b/weightgraph/img/scale.xcf differ
diff --git a/weightgraph/maemofiles/48/weightgraph.png b/weightgraph/maemofiles/48/weightgraph.png
new file mode 100644 (file)
index 0000000..d70dbd5
Binary files /dev/null and b/weightgraph/maemofiles/48/weightgraph.png differ
diff --git a/weightgraph/maemofiles/64/weightgraph.png b/weightgraph/maemofiles/64/weightgraph.png
new file mode 100644 (file)
index 0000000..ea5ad29
Binary files /dev/null and b/weightgraph/maemofiles/64/weightgraph.png differ
diff --git a/weightgraph/maemofiles/weightgraph.desktop b/weightgraph/maemofiles/weightgraph.desktop
new file mode 100644 (file)
index 0000000..403a543
--- /dev/null
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Encoding=UTF-8
+Version=1.0
+Type=Application
+Name=WeightGraph
+Exec=/usr/bin/weightgraph
+Icon=weightgraph
+X-Window-Icon=weightgraph
+X-Window-Icon-Dimmed=weightgraph
+Categories=Utility
diff --git a/weightgraph/main.cpp b/weightgraph/main.cpp
new file mode 100644 (file)
index 0000000..a3e58d7
--- /dev/null
@@ -0,0 +1,53 @@
+#include <QtGui/QApplication>
+#include <QDir>
+#include <QMainWindow>
+#include <QMessageBox>
+#include "mainwindow.h"
+#include "weightdata.h"
+#include "settings.h"
+#include "settingswindow.h"
+
+//For debugging
+#include "weightgraphview.h"
+
+//Global pointer to the weight data
+WeightDataModel *wdm;
+Settings *settings;
+
+int main(int argc, char *argv[])
+{
+  QCoreApplication::setApplicationName("WeightGraph");
+  QCoreApplication::setOrganizationDomain("vpu.me");
+  QApplication a(argc, argv);
+  QDir dir(Settings::datadir());
+  try {
+    if(!dir.exists())
+      dir.mkpath(Settings::datadir());
+  } catch(...) {
+    QMainWindow w2;
+    w2.show();
+    QMessageBox::critical(&w2, "Critical error",
+                          "Critical error while creating directory \""+Settings::datadir()+"\"");
+    return 1;
+  }
+  QString datafilename = Settings::datafile();
+  try {
+    wdm = new WeightDataModel(datafilename, &a);
+  }
+  catch(QString error) {
+    QMainWindow w2;
+    w2.show();
+    QMessageBox::critical(&w2, "Critical error",
+                          "Critical error while opening weight data file:\""+error+"\"");
+    return 1;
+  }
+
+  MainWindow w;
+#if defined(Q_WS_S60)
+  w.showMaximized();
+#else
+  w.show();
+#endif
+
+  return a.exec();
+}
diff --git a/weightgraph/mainwindow.cpp b/weightgraph/mainwindow.cpp
new file mode 100644 (file)
index 0000000..f4211e1
--- /dev/null
@@ -0,0 +1,164 @@
+//#include "ui_mainwindow.h"
+#include "mainwindow.h"
+#include "weightdata.h"
+#include "weightstatsview.h"
+#include "weightgraphview.h"
+#include <QtGui/QX11Info>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+extern WeightDataModel *wdm;
+
+MainWindow::MainWindow(QWidget *parent) :
+    QMainWindow(parent)
+{
+#ifdef Q_WS_MAEMO_5
+  setAttribute(Qt::WA_Maemo5StackedWindow);
+#endif
+
+  setWindowTitle("WeightGraph");
+
+  QWidget *central = new QWidget(this);
+  QHBoxLayout *topLayout = new QHBoxLayout(central);
+
+  // vv LEFT SIDE BEGINS vv
+  QWidget *leftContainer = new QWidget(central);
+  QVBoxLayout *leftLayout = new QVBoxLayout(leftContainer);
+  leftLayout->setSpacing(1);
+  leftLayout->setMargin(1);
+
+  // vvv LEFT TOP BEGINS vvv
+  //QGroupBox * leftTopContainer = new QGroupBox("Today's weight?", leftContainer);
+  QFrame *leftTopContainer = new QFrame(leftContainer);
+  leftTopContainer->setFrameShadow(QFrame::Sunken);
+  leftTopContainer->setFrameStyle(QFrame::StyledPanel);
+  leftTopContainer->setLineWidth(2);
+  leftTopContainer->setMidLineWidth(2);
+  QGridLayout *leftTopLayout = new QGridLayout(leftTopContainer);
+
+  QLabel *todayLabel = new QLabel("Today's weight?", leftContainer);
+  leftTopLayout->addWidget(todayLabel, 0, 0);
+
+  weight = new WeightSpinBox(leftTopContainer);
+  if(wdm->size() > 0)
+    weight->setValue(wdm->getWeights().last().weight);
+  leftTopLayout->addWidget(weight, 1, 0);
+
+  QPushButton *setButton = new QPushButton("Set", leftTopContainer);
+  connect(setButton, SIGNAL(clicked()), this, SLOT(setTodaysWeight()));
+  leftTopLayout->addWidget(setButton, 2, 0);
+
+  leftLayout->addWidget(leftTopContainer);
+  // ^^^ LEFT TOP ENDS ^^^
+
+//  QWidget *vspacer0 = new QWidget(leftContainer);
+//  vspacer0->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
+//  leftLayout->addWidget(vspacer0);
+
+  WeightStatsView *stats = new WeightStatsView(wdm, this);
+  leftLayout->addWidget(stats);
+
+//  QWidget *vspacer = new QWidget(leftContainer);
+//  vspacer->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
+//  leftLayout->addWidget(vspacer);
+
+  ew = new EditWindow(this);
+  QPushButton *listButton = new QPushButton("List / edit", leftContainer);
+  connect(listButton, SIGNAL(clicked()), ew, SLOT(show()));
+  leftLayout->addWidget(listButton);
+
+  topLayout->addWidget(leftContainer);
+  // ^^ LEFT SIDE ENDS ^^
+
+  smallGraph = new WeightGraphView(wdm, "Small", central);
+  bigGraph = new WeightGraphView(wdm, "Big", this);
+#ifdef Q_WS_MAEMO_5
+  bigGraph->setAttribute(Qt::WA_Maemo5StackedWindow);
+  bigGraph->grabZoomKeys(Settings::grabZoomKeys());
+#endif
+  bigGraph->setWindowFlags(bigGraph->windowFlags() | Qt::Window);
+  connect(smallGraph, SIGNAL(clicked()), bigGraph, SLOT(show()));
+  topLayout->addWidget(smallGraph);
+
+  this->setCentralWidget(central);
+
+
+  aboutDialog = new QMessageBox(QMessageBox::NoIcon, "About WeightGraph",
+                                "Copyright (C) 2011 Visa Putkinen. Licence: GPLv2",
+                                QMessageBox::Close, this);
+  aboutDialog->setIconPixmap(QPixmap(":/img/icon48"));
+  aboutDialog->setInformativeText("Usage: enter your weight every day using "
+                                  "the main screen's \"Today's weight?\" box "
+                                  "or the List / edit window. You may enter "
+                                  "at most one weight per day."
+                                  "\n\n"
+                                  "A graph of the weights will be drawn when "
+                                  "two or more weights are entered. Tap the "
+                                  "graph to open a larger graph view. Use the "
+                                  "external zoom buttons to adjust the shown "
+                                  "time period."
+                                  "\n\n"
+                                  "The weights are stored in human readable (and "
+                                  "writeable) form in "
+                                  "MyDocs/WeightGraph/weightdata.txt");
+
+
+  //Important: SettingsWindow must be created after all graph
+  //views are created or settings won't show all graphs
+  settingsWindow = new SettingsWindow(this);
+  menuBar()->addAction(tr("Settings"), settingsWindow, SLOT(show()));
+  menuBar()->addAction(tr("About"), aboutDialog, SLOT(exec()));
+
+  grabZoomKeys(Settings::grabZoomKeys());
+
+  connect(Settings::self(), SIGNAL(settingChanged()), this, SLOT(update()));
+}
+
+void MainWindow::setTodaysWeight()
+{
+  wdm->setWeightForDate(QDate::currentDate(), weight->value());
+}
+
+void MainWindow::keyPressEvent(QKeyEvent* event)
+{
+  //qDebug() << "Main window: key pressed: " << event->key();
+  switch (event->key()) {
+  case Qt::Key_F7:
+      smallGraph->decPeriod();
+      event->accept();
+      break;
+
+  case Qt::Key_F8:
+      smallGraph->incPeriod();
+      event->accept();
+      break;
+  }
+  QWidget::keyPressEvent(event);
+}
+void MainWindow::grabZoomKeys(bool grab)
+{
+  if (!winId()) {
+    qWarning("Can't grab keys unless we have a window id");
+    return;
+  }
+
+  unsigned long val = (grab) ? 1 : 0;
+  Atom atom = XInternAtom(QX11Info::display(), "_HILDON_ZOOM_KEY_ATOM", False);
+  if (!atom) {
+    qWarning("Unable to obtain _HILDON_ZOOM_KEY_ATOM. This example will only work "
+             "on a Maemo 5 device!");
+    return;
+  }
+
+
+  XChangeProperty (QX11Info::display(),
+                   winId(),
+                   atom,
+                   XA_INTEGER,
+                   32,
+                   PropModeReplace,
+                   reinterpret_cast<unsigned char *>(&val),
+                   1);
+
+  //qDebug() << "Main window grabbed zoom keys: " << winId();
+}
diff --git a/weightgraph/mainwindow.h b/weightgraph/mainwindow.h
new file mode 100644 (file)
index 0000000..ecb8c10
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QtGui>
+#include "editwindow.h"
+#include "weightspinbox.h"
+#include "weightgraphview.h"
+#include "settingswindow.h"
+
+namespace Ui {
+    class MainWindow;
+}
+
+class MainWindow : public QMainWindow
+{
+  Q_OBJECT
+
+public:
+  explicit MainWindow(QWidget *parent = 0);
+public slots:
+  void grabZoomKeys(bool grab);
+private slots:
+  void setTodaysWeight();
+  void update() {
+    grabZoomKeys(Settings::grabZoomKeys());
+    QWidget::update();
+  }
+protected:
+  void keyPressEvent(QKeyEvent* event);
+private:
+  //Ui::MainWindow *ui;
+  WeightSpinBox *weight;
+  EditWindow *ew;
+  WeightGraphView *smallGraph;
+  WeightGraphView *bigGraph;
+  SettingsWindow *settingsWindow;
+  QMessageBox *aboutDialog;
+};
+
+#endif // MAINWINDOW_H
diff --git a/weightgraph/mainwindow.ui b/weightgraph/mainwindow.ui
new file mode 100644 (file)
index 0000000..2c067ca
--- /dev/null
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>800</width>
+    <height>480</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralWidget">
+   <widget class="QWidget" name="horizontalLayoutWidget">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>10</y>
+      <width>741</width>
+      <height>461</height>
+     </rect>
+    </property>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_2">
+         <item>
+          <widget class="QLabel" name="todayLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string>Today:</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QDoubleSpinBox" name="todayWeight">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="buttonSymbols">
+            <enum>QAbstractSpinBox::UpDownArrows</enum>
+           </property>
+           <property name="specialValueText">
+            <string>kg</string>
+           </property>
+           <property name="decimals">
+            <number>1</number>
+           </property>
+           <property name="minimum">
+            <double>30.000000000000000</double>
+           </property>
+           <property name="maximum">
+            <double>1000.000000000000000</double>
+           </property>
+           <property name="singleStep">
+            <double>0.100000000000000</double>
+           </property>
+           <property name="value">
+            <double>70.000000000000000</double>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLabel" name="kgLabel">
+           <property name="text">
+            <string>Kg</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <widget class="QLabel" name="label_2">
+         <property name="text">
+          <string>TextLabel1</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="label_3">
+         <property name="text">
+          <string>TextLabel2</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <spacer name="verticalSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="enabled">
+        <bool>true</bool>
+       </property>
+       <property name="autoFillBackground">
+        <bool>true</bool>
+       </property>
+       <property name="frameShape">
+        <enum>QFrame::Box</enum>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="pixmap">
+        <pixmap resource="res.qrc">:/img/graph.jpg</pixmap>
+       </property>
+       <property name="scaledContents">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </widget>
+  </widget>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources>
+  <include location="res.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/weightgraph/makepackage.sh b/weightgraph/makepackage.sh
new file mode 100755 (executable)
index 0000000..6249836
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+MAD=/opt/NokiaQtSDK/Maemo/4.6.2/bin/mad
+APPNAME=weightgraph
+VERSION=`head -1 debian/changelog | cut -d'(' -f 2 | cut -d')' -f 1`
+DIR=/tmp/$APPNAME-$VERSION
+rm -rf $DIR
+cp -r . $DIR
+cd $DIR
+$MAD dpkg-buildpackage
+$MAD dpkg-buildpackage -sa -S
diff --git a/weightgraph/res.qrc b/weightgraph/res.qrc
new file mode 100644 (file)
index 0000000..0bb0aa9
--- /dev/null
@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/img">
+        <file alias="icon48">maemofiles/48/weightgraph.png</file>
+    </qresource>
+</RCC>
diff --git a/weightgraph/settings.cpp b/weightgraph/settings.cpp
new file mode 100644 (file)
index 0000000..b88ff88
--- /dev/null
@@ -0,0 +1,167 @@
+#include "settings.h"
+#include <QDir>
+
+Settings *Settings::singleton = new Settings();
+QSettings Settings::s(Settings::settingsfile(), QSettings::IniFormat);
+const QString Settings::weightUnitKey = "WeightUnit";
+const QString Settings::goalWeightMinKey = "GoalWeightMin";
+const QString Settings::goalWeightMaxKey = "GoalWeightMax";
+const QString Settings::grabZoomKeysKey = "GrabZoomKeys";
+const QString Settings::graphSettingsGroupSuffix = "Graph";
+const QString Settings::goalWeightEnabledKey = "GoalWeightEnabled";
+const QString Settings::weightIntervalModeKey = "WeightIntervalMode";
+const QString Settings::weightIntervalMinKey = "WeightIntervalMin";
+const QString Settings::weightIntervalMaxKey = "WeightIntervalMax";
+const QString Settings::defaultTimeIntervalKey = "DefaultTimeInterval";
+
+QString Settings::weightUnit()
+{
+  if(!s.contains(weightUnitKey))
+    setWeightUnit("kg");
+  return s.value(weightUnitKey).toString();
+}
+void Settings::setWeightUnit(QString wu)
+{
+  s.setValue(weightUnitKey, wu);
+}
+void Settings::setWeightUnitAndSync(QString wu)
+{
+  setWeightUnit(wu);
+  sync();
+}
+
+double Settings::goalWeightMin()
+{
+  if(!s.contains(goalWeightMinKey))
+    setGoalWeightMin(0.0);
+  return s.value(goalWeightMinKey).toDouble();
+}
+void Settings::setGoalWeightMin(double min)
+{
+  s.setValue(goalWeightMinKey, min);
+}
+void Settings::setGoalWeightMinAndSync(double min)
+{
+  setGoalWeightMin(min);
+  sync();
+}
+
+double Settings::goalWeightMax()
+{
+  if(!s.contains(goalWeightMaxKey))
+    setGoalWeightMax(0.0);
+  return s.value(goalWeightMaxKey).toDouble();
+}
+void Settings::setGoalWeightMax(double max)
+{
+  s.setValue(goalWeightMaxKey, max);
+}
+void Settings::setGoalWeightMaxAndSync(double max)
+{
+  setGoalWeightMax(max);
+  sync();
+}
+
+bool Settings::grabZoomKeys()
+{
+  if(!s.contains(grabZoomKeysKey))
+    setGrabZoomKeys(true);
+  return s.value(grabZoomKeysKey).toBool();
+}
+void Settings::setGrabZoomKeys(bool grab)
+{
+  s.setValue(grabZoomKeysKey, grab);
+}
+void Settings::setGrabZoomKeysAndSync(bool grab)
+{
+  setGrabZoomKeys(grab);
+  sync();
+}
+
+GraphSettings Settings::graphSettings(const QString &graphId)
+{
+  GraphSettings ret;
+  bool changed = false;
+  s.beginGroup(graphId+graphSettingsGroupSuffix);
+
+  if (!s.contains(goalWeightEnabledKey)) {
+    s.setValue(goalWeightEnabledKey, false);
+    changed = true;
+  }
+  ret.goalWeightEnabled = s.value(goalWeightEnabledKey).toBool();
+
+  if (!s.contains(weightIntervalModeKey)) {
+    s.setValue(weightIntervalModeKey, (int)GraphSettings::AutomaticWithoutGoalWeight);
+    changed = true;
+  }
+  ret.weightIntervalMode =
+      (GraphSettings::WeightIntervalMode)s.value(weightIntervalModeKey).toInt();
+
+  if (!s.contains(weightIntervalMinKey)) {
+    s.setValue(weightIntervalMinKey, 0.0);
+    changed = true;
+  }
+  ret.weightIntervalMin = s.value(weightIntervalMinKey).toDouble();
+
+  if (!s.contains(weightIntervalMaxKey)) {
+    s.setValue(weightIntervalMaxKey, 0.0);
+    changed = true;
+  }
+  ret.weightIntervalMax = s.value(weightIntervalMaxKey).toDouble();
+
+  if (!s.contains(defaultTimeIntervalKey)) {
+    s.setValue(defaultTimeIntervalKey, 0);
+    changed = true;
+  }
+  ret.defaultTimeInterval = s.value(defaultTimeIntervalKey).toInt();
+
+  s.endGroup();
+  if (changed)
+    sync();
+  return ret;
+}
+void Settings::setGraphSettings(const QString &graphId, GraphSettings &gs)
+{
+  s.beginGroup(graphId+graphSettingsGroupSuffix);
+  s.setValue(goalWeightEnabledKey, gs.goalWeightEnabled);
+  s.setValue(weightIntervalModeKey, (int)gs.weightIntervalMode);
+  s.setValue(weightIntervalMinKey, gs.weightIntervalMin);
+  s.setValue(weightIntervalMaxKey, gs.weightIntervalMax);
+  s.setValue(defaultTimeIntervalKey, gs.defaultTimeInterval);
+  s.endGroup();
+}
+void Settings::setGraphSettingsAndSync(const QString &graphId,
+                                       GraphSettings &gs)
+{
+  setGraphSettings(graphId, gs);
+  sync();
+}
+
+QStringList Settings::graphIds()
+{
+  QStringList tmp = s.childGroups();
+  tmp = tmp.filter(QRegExp("^\\w+Graph$"));
+  return tmp.replaceInStrings(QRegExp("^(\\w+)Graph$"), "\\1");
+}
+void Settings::sync()
+{
+  s.sync();
+  emit singleton->settingChanged();
+}
+
+QString Settings::datadir()
+{
+#ifdef Q_WS_MAEMO_5
+  return QDir::home().path()+"/MyDocs/WeightGraph";
+#else
+  return QDir::home().path()+"/.weightgraph";
+#endif
+}
+QString Settings::datafile()
+{
+  return datadir()+"/weightdata.txt";
+}
+QString Settings::settingsfile()
+{
+  return datadir()+"/config.ini";
+}
diff --git a/weightgraph/settings.h b/weightgraph/settings.h
new file mode 100644 (file)
index 0000000..3339874
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef SETTINGS_H
+#define SETTINGS_H
+
+#include <QSettings>
+
+struct GraphSettings
+{
+  enum WeightIntervalMode {
+    AutomaticWithGoalWeight,
+    AutomaticWithoutGoalWeight,
+    Manual
+  };
+  bool goalWeightEnabled;
+  WeightIntervalMode weightIntervalMode;
+  double weightIntervalMin;
+  double weightIntervalMax;
+  int defaultTimeInterval;
+};
+
+class Settings : public QObject
+{
+  Q_OBJECT
+public:
+  static Settings *self() { return singleton; }
+  static QString weightUnit();
+  static double goalWeightMin();
+  static double goalWeightMax();
+  static bool grabZoomKeys();
+  static GraphSettings graphSettings(const QString &graphId);
+  static QStringList graphIds();
+
+  static QString datadir();
+  static QString datafile();
+  static QString settingsfile();
+public slots:
+  static void setWeightUnit(QString wu);
+  static void setWeightUnitAndSync(QString wu);
+  static void setGoalWeightMin(double min);
+  static void setGoalWeightMinAndSync(double min);
+  static void setGoalWeightMax(double max);
+  static void setGoalWeightMaxAndSync(double max);
+  static void setGrabZoomKeys(bool grab);
+  static void setGrabZoomKeysAndSync(bool grab);
+  static void setGraphSettings(const QString &graphId, GraphSettings &gs);
+  static void setGraphSettingsAndSync(const QString &graphId,
+                                      GraphSettings &gs);
+  static void sync();
+signals:;
+  //settingChanged is emitted when sync() is called.
+  void settingChanged();
+private:
+  Settings() : QObject(0) {}
+  static Settings *singleton;
+  static QSettings s;
+  static const QString weightUnitKey;
+  static const QString goalWeightMinKey;
+  static const QString goalWeightMaxKey;
+  static const QString grabZoomKeysKey;
+  static const QString graphSettingsGroupSuffix;
+  static const QString goalWeightEnabledKey;
+  static const QString weightIntervalModeKey;
+  static const QString weightIntervalMinKey;
+  static const QString weightIntervalMaxKey;
+  static const QString defaultTimeIntervalKey;
+};
+#endif // SETTINGS_H
diff --git a/weightgraph/settingswindow.cpp b/weightgraph/settingswindow.cpp
new file mode 100644 (file)
index 0000000..781a3b6
--- /dev/null
@@ -0,0 +1,251 @@
+#include "settingswindow.h"
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+#include <QGridLayout>
+#include <QLabel>
+#include <QStringListModel>
+#include <QPushButton>
+
+//for debugging
+#include <iostream>
+#include <QDebug>
+
+static QStringList timeIntervalStrings() {
+  QStringList timeIntervals;
+  timeIntervals.append("1 week");
+  timeIntervals.append("1 month");
+  timeIntervals.append("3 months");
+  timeIntervals.append("6 months");
+  timeIntervals.append("1 year");
+  timeIntervals.append("all");
+  return timeIntervals;
+}
+static int timeIntervalToIndex(int days) {
+  switch(days) {
+  case 0: return 5;
+  case 7: return 0;
+  case 30: return 1;
+  case 90: return 2;
+  case 180: return 3;
+  case 365: return 4;
+  default: Q_ASSERT(0 && "unknown time interval");
+  }
+  return 0; //unreachable
+}
+static int indexToTimeInterval(int index) {
+  switch(index) {
+  case 5: return 0;
+  case 0: return 7;
+  case 1: return 30;
+  case 2: return 90;
+  case 3: return 180;
+  case 4: return 365;
+  default: Q_ASSERT(0 && "unknown time interval index");
+  }
+  return 0; //unreachable
+}
+
+SettingsWindow::SettingsWindow(QWidget *parent) :
+    QMainWindow(parent)
+{
+#ifdef Q_WS_MAEMO_5
+  setAttribute(Qt::WA_Maemo5StackedWindow);
+#endif
+
+  setWindowTitle("WeightGraph");
+
+  QWidget *rootContainer = new QWidget(this);
+  QVBoxLayout *rootLayout = new QVBoxLayout(rootContainer);
+
+  QWidget *topContainer = new QWidget(rootContainer);
+  QGridLayout *topLayout = new QGridLayout(topContainer);
+
+  QStringList units; units.append("kg"); units.append("lb");
+  QStringListModel *weightUnitModel = new QStringListModel(units, topContainer);
+#ifdef Q_WS_MAEMO_5
+  weightUnit = new QMaemo5ValueButton("Unit", topContainer);
+  weightUnit->setValueLayout(QMaemo5ValueButton::ValueUnderTextCentered);
+  QMaemo5ListPickSelector *weightUnitSelector = new QMaemo5ListPickSelector(topContainer);
+  weightUnitSelector->setModel(weightUnitModel);
+  weightUnitSelector->setCurrentIndex(Settings::weightUnit() == "kg" ? 0 : 1);
+  weightUnit->setPickSelector(weightUnitSelector);
+  connect(weightUnit->pickSelector(), SIGNAL(selected(QString)),
+          Settings::self(), SLOT(setWeightUnitAndSync(QString)));
+#else
+  weightUnit = new QComboBox(topContainer);
+  weightUnit->setModel(weightUnitModel);
+#endif
+
+  topLayout->addWidget(weightUnit, 0, 0);
+
+//  QWidget *spacer = new QWidget(topContainer);
+//  spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
+//  topLayout->addWidget(spacer);
+
+  QFrame *goalFrame = new QFrame(topContainer);
+  goalFrame->setFrameShadow(QFrame::Sunken);
+  goalFrame->setFrameStyle(QFrame::StyledPanel);
+  goalFrame->setLineWidth(2);
+  goalFrame->setMidLineWidth(2);
+  QHBoxLayout *goalLayout = new QHBoxLayout(goalFrame);
+
+  goalLayout->addWidget(new QLabel("Goal weight:", topContainer));
+
+  goalMin = new WeightSpinBox(topContainer);
+  goalMin->setValue(Settings::goalWeightMin());
+  goalLayout->addWidget(goalMin);
+
+  goalLayout->addWidget(new QLabel("-", topContainer));
+
+  goalMax = new WeightSpinBox(topContainer);
+  goalMax->setValue(Settings::goalWeightMax());
+  goalLayout->addWidget(goalMax);
+  topLayout->addWidget(goalFrame, 0, 1);
+
+  grabZoomKeys = new QPushButton("Use zoom/volume keys to zoom", rootContainer);
+  grabZoomKeys->setCheckable(true);
+  grabZoomKeys->setChecked(Settings::grabZoomKeys());
+  topLayout->addWidget(grabZoomKeys, 1, 0, 1, 2);
+
+  rootLayout->addWidget(topContainer);
+
+  QTabWidget *tabWidget = new QTabWidget(rootContainer);
+
+  graphSettingsList = new QList<GraphSettingsWidget*>();
+
+  QStringList graphIds; graphIds.append("Small"); graphIds.append("Big");
+  foreach(QString id, graphIds) {
+    GraphSettingsWidget *gsw = new GraphSettingsWidget(id, tabWidget);
+    graphSettingsList->append(gsw);
+    tabWidget->addTab(gsw, id+" graph");
+  }
+
+  rootLayout->addWidget(tabWidget);
+
+  setCentralWidget(rootContainer);
+}
+
+void SettingsWindow::closeEvent(QCloseEvent *event)
+{
+  //Note: Weight unit not saved as it is saved on change.
+  Settings::setGoalWeightMin(goalMin->value());
+  Settings::setGoalWeightMax(goalMax->value());
+  Settings::setGrabZoomKeys(grabZoomKeys->isChecked());
+  foreach(GraphSettingsWidget *gsw, *graphSettingsList) {
+    GraphSettings gs;
+    gs.goalWeightEnabled = gsw->goalWeightEnabled;
+#ifdef Q_WS_MAEMO_5
+    QMaemo5ListPickSelector *picker =
+        dynamic_cast<QMaemo5ListPickSelector*>(gsw->weightIntervalMode->pickSelector());
+    gs.weightIntervalMode = (GraphSettings::WeightIntervalMode)picker->currentIndex();
+#else
+    gs.weightIntervalMode =
+        (GraphSettings::WeightIntervalMode)gsw->weightIntervalMode->currentIndex();
+#endif
+    gs.weightIntervalMin = gsw->weightIntervalMin->value();
+    gs.weightIntervalMax = gsw->weightIntervalMax->value();
+#ifdef Q_WS_MAEMO_5
+    QMaemo5ListPickSelector *picker2 =
+        dynamic_cast<QMaemo5ListPickSelector*>(gsw->defaultTimeInterval->pickSelector());
+    gs.defaultTimeInterval = indexToTimeInterval(picker2->currentIndex());
+#else
+    gs.defaultTimeInterval
+        = indexToTimeInterval(gsw->defaultTimeInterval->currentIndex());
+#endif
+    Settings::setGraphSettingsAndSync(gsw->graphId, gs);
+  }
+  QMainWindow::closeEvent(event);
+}
+
+GraphSettingsWidget::GraphSettingsWidget(QString graphId, QWidget *parent) :
+    QWidget(parent), graphId(graphId) {
+  QWidget *rootContainer = new QWidget(this);
+  QGridLayout *rootLayout = new QGridLayout(rootContainer);
+
+  QWidget *topSpacer = new QWidget(rootContainer);
+  topSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+  rootLayout->addWidget(topSpacer, 0, 0);
+
+  GraphSettings gs = Settings::graphSettings(graphId);
+
+  goalWeightEnabled = new QPushButton("Show goal weight", rootContainer);
+  goalWeightEnabled->setCheckable(true);
+  goalWeightEnabled->setChecked(gs.goalWeightEnabled);
+  rootLayout->addWidget(goalWeightEnabled, 1, 0);
+
+  QStringListModel *timeIntervalModel =
+      new QStringListModel(timeIntervalStrings(), rootContainer);
+#ifdef Q_WS_MAEMO_5
+  defaultTimeInterval= new QMaemo5ValueButton("Time interval", rootContainer);
+  defaultTimeInterval->setValueLayout(QMaemo5ValueButton::ValueUnderTextCentered);
+  QMaemo5ListPickSelector *timeIntervalSelector =
+      new QMaemo5ListPickSelector(rootContainer);
+  timeIntervalSelector->setModel(timeIntervalModel);
+  timeIntervalSelector->setCurrentIndex(timeIntervalToIndex(gs.defaultTimeInterval));
+  defaultTimeInterval->setPickSelector(timeIntervalSelector);
+#else
+  defaultTimeInterval = new QComboBox(rootContainer);
+  defaultTimeInterval->setModel(timeIntervalModel);
+  defaultTimeInterval->setCurrentIndex(timeIntervalToIndex(gs.defaultTimeInterval));
+#endif
+  rootLayout->addWidget(defaultTimeInterval, 2, 0);
+
+//  QWidget *spacer = new QWidget(rootContainer);
+//  spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
+//  rootLayout->addWidget(spacer, 0, 1, 2, 1);
+
+  QFrame *weightFrame = new QFrame(rootContainer);
+  weightFrame->setFrameShadow(QFrame::Sunken);
+  weightFrame->setFrameStyle(QFrame::StyledPanel);
+  weightFrame->setLineWidth(2);
+  weightFrame->setMidLineWidth(2);
+  QGridLayout *weightLayout = new QGridLayout(weightFrame);
+
+  QStringList weightIntervalModes;
+  weightIntervalModes.append("Auto with goal weight");
+  weightIntervalModes.append("Auto without goal weight");
+  weightIntervalModes.append("Manual");
+  QStringListModel *weightIntervalModeModel =
+      new QStringListModel(weightIntervalModes, rootContainer);
+#ifdef Q_WS_MAEMO_5
+  weightIntervalMode = new QMaemo5ValueButton("Weight interval mode", rootContainer);
+  weightIntervalMode->setValueLayout(QMaemo5ValueButton::ValueUnderTextCentered);
+  QMaemo5ListPickSelector *weightIntervalModeSelector =
+      new QMaemo5ListPickSelector(rootContainer);
+  weightIntervalModeSelector->setModel(weightIntervalModeModel);
+  weightIntervalModeSelector->setCurrentIndex((int)gs.weightIntervalMode);
+  weightIntervalMode->setPickSelector(weightIntervalModeSelector);
+  connect(weightIntervalMode->pickSelector(), SIGNAL(selected(QString)),
+          this, SLOT(weightIntervalModeChanged(QString)));
+#else
+  weightIntervalMode = new QComboBox(rootContainer);
+  weightIntervalMode->setModel(weightIntervalModeModel);
+#endif
+  weightLayout->addWidget(weightIntervalMode, 0, 0, 1, 3);
+
+
+  weightIntervalMin = new WeightSpinBox(rootContainer);
+  weightIntervalMin->setEnabled(gs.weightIntervalMode == GraphSettings::Manual);
+  weightIntervalMin->setValue(gs.weightIntervalMin);
+  weightLayout->addWidget(weightIntervalMin, 1, 0);
+
+  weightLayout->addWidget(new QLabel("-", rootContainer), 1, 1);
+
+  weightIntervalMax = new WeightSpinBox(rootContainer);
+  weightIntervalMax->setEnabled(gs.weightIntervalMode == GraphSettings::Manual);
+  weightIntervalMax->setValue(gs.weightIntervalMax);
+  weightLayout->addWidget(weightIntervalMax, 1, 2);
+
+  rootLayout->addWidget(weightFrame, 1, 1, 2, 1);
+
+  QWidget *bottomSpacer = new QWidget(rootContainer);
+  bottomSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+  rootLayout->addWidget(bottomSpacer, 4, 0);
+
+}
+void GraphSettingsWidget::weightIntervalModeChanged(QString mode) {
+  bool isManual = mode == "Manual";
+  weightIntervalMin->setEnabled(isManual);
+  weightIntervalMax->setEnabled(isManual);
+}
+
diff --git a/weightgraph/settingswindow.h b/weightgraph/settingswindow.h
new file mode 100644 (file)
index 0000000..45c207a
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef SETTINGSWINDOW_H
+#define SETTINGSWINDOW_H
+
+#include <QMainWindow>
+#include <QTabWidget>
+#include <QPushButton>
+#include <QList>
+#ifdef Q_WS_MAEMO_5
+# include <QtMaemo5>
+#else
+# include <QComboBox>
+#endif
+#include "weightspinbox.h"
+
+class GraphSettingsWidget;
+
+class SettingsWindow : public QMainWindow
+{
+  Q_OBJECT
+public:
+  explicit SettingsWindow(QWidget *parent = 0);
+protected:
+  void closeEvent(QCloseEvent *);
+signals:
+
+public slots:
+private:
+#ifdef Q_WS_MAEMO_5
+  QMaemo5ValueButton *weightUnit;
+#else
+  QComboBox *weightUnit;
+#endif
+  WeightSpinBox *goalMin;
+  WeightSpinBox *goalMax;
+  QPushButton *grabZoomKeys;
+  QList<GraphSettingsWidget*> *graphSettingsList;
+};
+
+class GraphSettingsWidget : public QWidget {
+  Q_OBJECT
+public:
+  GraphSettingsWidget(QString graphId, QWidget *parent);
+private slots:
+  void weightIntervalModeChanged(QString mode);
+public:
+  QString graphId;
+  QPushButton *goalWeightEnabled;
+#ifdef Q_WS_MAEMO_5
+  QMaemo5ValueButton *weightIntervalMode;
+  QMaemo5ValueButton *defaultTimeInterval;
+#else
+  QComboBox *weightIntervalMode;
+  QComboBox *defaultTimeInterval;
+#endif
+
+  WeightSpinBox *weightIntervalMin;
+  WeightSpinBox *weightIntervalMax;
+};
+
+#endif // SETTINGSWINDOW_H
diff --git a/weightgraph/weightdata.cpp b/weightgraph/weightdata.cpp
new file mode 100644 (file)
index 0000000..36b286c
--- /dev/null
@@ -0,0 +1,230 @@
+#include "weightdata.h"
+#include "settings.h"
+#include <QTextStream>
+#include <QStringList>
+#include <QtAlgorithms>
+#include <assert.h>
+#include <limits>
+
+#include <QDebug>
+
+WeightDataModel::WeightDataModel(QString &datafilename, QObject *parent) :
+    QAbstractTableModel(parent), datafile(datafilename)
+{
+  clear();
+  readFromDisk();
+}
+
+void WeightDataModel::clear()
+{
+  weights.clear();
+}
+
+Qt::ItemFlags WeightDataModel::flags(const QModelIndex &index) const
+{
+  if(!index.isValid())
+    return Qt::ItemIsEnabled;
+  switch(index.column()) {
+    case 0: return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+    case 1: return Qt::ItemIsEnabled | Qt::ItemIsEditable;
+    default: return Qt::NoItemFlags;
+  }
+}
+
+QVariant WeightDataModel::data(const QModelIndex &index, int role) const
+{
+  if (!index.isValid() || index.row() >= rowCount(QModelIndex())
+      || index.column() >= columnCount(QModelIndex()))
+    return QVariant();
+
+  if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::SizeHintRole)
+    return QVariant();
+
+  if (role == Qt::SizeHintRole) {
+    if (index.column() == 0)
+      return QSize(230,70);
+    else
+      return QSize(280,70);
+  }
+
+  if (index.column() == 0) {
+    return weights.at(index.row()).date.toString(Qt::ISODate);
+  }
+  else {
+    if (role == Qt::DisplayRole)
+      return QString("%1 %2").arg(weights.at(index.row()).weight,5,'f',1)
+                             .arg(Settings::weightUnit());
+    else
+      return weights.at(index.row()).weight;
+  }
+
+  return QVariant();
+}
+
+bool WeightDataModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+  if (!index.isValid() || role != Qt::EditRole)
+    return false;
+  switch (index.column()) {
+  case 0:
+    weights[index.row()].date = value.toDate();
+    break;
+  case 1: {
+    double weight = value.toDouble();
+    weights[index.row()].weight = weight;
+    break;
+  }
+  default: return false;
+  }
+
+  emit(dataChanged(index, index));
+
+  writeToDisk();
+  return true;
+}
+
+bool WeightDataModel::setDataForRow(int row, const DateWeight &dw)
+{
+  if (row < 0 || row >= weights.size())
+    return false;
+
+  weights[row] = dw;
+  QModelIndex a = index(row, 0);
+  QModelIndex b = index(row, 1);
+
+  emit(dataChanged(a, b));
+  writeToDisk();
+  return true;
+}
+// Sets data for a date. Adds a new row for the date if it doesn't exist.
+void WeightDataModel::setWeightForDate(const QDate &date, double weight)
+{
+  int row = rowOfDate(date);
+  if (row == -1) {
+    row = rowForNewDate(date);
+    insertRows(row, 1);
+  }
+  DateWeight dw = {date, weight};
+  setDataForRow(row, dw);
+}
+
+void WeightDataModel::setWeightForDate(const WeightDataModel::DateWeight &dw)
+{
+  setWeightForDate(dw.date, dw.weight);
+}
+
+QVariant WeightDataModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+  if (role != Qt::DisplayRole)
+    return QVariant();
+  if (orientation == Qt::Horizontal) {
+    return section == 0 ? tr("Date") : tr("Weight");
+  }
+  else {
+    return QString("%1").arg(section);
+  }
+}
+
+// Insert count "empty" rows starting from row.
+// DOESN'T WRITE DATA BACK TO DISK to allow inserter
+// to modify the row, then write it.
+bool WeightDataModel::insertRows(int row, int count, const QModelIndex &/*parent*/)
+{
+  beginInsertRows(QModelIndex(), row, row+count-1);
+  DateWeight empty;
+  //empty.date.setDate(2000,1,1);
+  empty.weight = 0.0;
+  while(count--)
+    weights.insert(row, empty);
+  endInsertRows();
+  return true;
+}
+
+bool WeightDataModel::removeRows(int row, int count, const QModelIndex &/*parent*/)
+{
+  beginRemoveRows(QModelIndex(), row, row+count-1);
+  while(count--)
+    weights.removeAt(row);
+  endRemoveRows();
+  writeToDisk();
+  return true;
+}
+
+//Returns the row of the date or -1 if it doesn't exist.
+int WeightDataModel::rowOfDate(const QDate &date) const
+{
+  for(int i=0; i<weights.size(); i++)
+    if (weights[i].date == date)
+      return i;
+  return -1;
+}
+
+bool WeightDataModel::dateExists(const QDate &date) const
+{
+  return rowOfDate(date) != -1;
+}
+
+int WeightDataModel::rowForNewDate(const QDate &date) const
+{
+  if (weights.size() == 0)
+    return 0;
+  if (date < weights.first().date)
+    return 0;
+  for(int i=1; i<weights.size(); i++) {
+    if (weights.at(i-1).date < date
+        && weights.at(i).date > date)
+      return i;
+  }
+  if (date > weights.last().date)
+    return weights.size();
+  assert(0 && "UNREACHABLE");
+  return -1;
+}
+
+void WeightDataModel::writeToDisk()
+{
+  if (QFile::exists(datafile.fileName())) {
+    QFile::copy(datafile.fileName(), datafile.fileName()+".bak");
+  }
+  if (datafile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+    QTextStream stream(&datafile);
+    foreach(DateWeight w, weights)
+      stream << w.date.toString(Qt::ISODate) << ';' << w.weight << "\n";
+    datafile.close();
+  }
+}
+
+void WeightDataModel::readFromDisk()
+{
+  if (!datafile.exists()) {
+    clear();
+    return;
+  }
+  if (datafile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+    clear();
+    QTextStream stream(&datafile);
+    QString line = stream.readLine();
+    while (!line.isNull()) {
+      QStringList parts = line.split(';');
+      DateWeight w;
+      w.date = QDate::fromString(parts[0], Qt::ISODate);
+      if (!w.date.isValid()) {
+        throw(QString("Invalid date in file: '%1'").arg(parts[0]));
+      }
+      bool ok;
+      w.weight = parts[1].toDouble(&ok);
+      if(!ok) {
+        throw(QString("Invalid weight in file: '%1'").arg(parts[1]));
+      }
+      weights.push_back(w);
+      line = stream.readLine();
+    }
+    datafile.close();
+    qSort(weights);
+  }
+  else {
+    clear();
+    throw(QString("Could not read file '%1'").arg(datafile.fileName()));
+    return;
+  }
+}
diff --git a/weightgraph/weightdata.h b/weightgraph/weightdata.h
new file mode 100644 (file)
index 0000000..41ded86
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef WEIGHTDATA_H
+#define WEIGHTDATA_H
+
+#include <QAbstractTableModel>
+#include <QDate>
+#include <QList>
+#include <QFile>
+#include <QDoubleSpinBox>
+#include <QItemEditorCreatorBase>
+
+//For debugging:
+#include <iostream>
+
+// A table model with 2 columns: Date | Weight
+class WeightDataModel : public QAbstractTableModel
+{
+  Q_OBJECT
+
+public:
+  struct DateWeight
+  {
+    QDate date;
+    double weight;
+    bool operator<(const DateWeight &o) const { return date < o.date; }
+  };
+  typedef QList<DateWeight> WeightList;
+  WeightDataModel(QString &datafile, QObject *parent = 0);
+  int rowCount(const QModelIndex &/**/) const { return weights.size(); }
+  int columnCount(const QModelIndex &/**/) const { return 2; }
+  Qt::ItemFlags flags(const QModelIndex &index) const;
+  QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+  bool setData(const QModelIndex &index, const QVariant &value, int role);
+  bool setDataForRow(int row, const DateWeight &dw);
+  void setWeightForDate(const QDate &date, double weight);
+  void setWeightForDate(const DateWeight &dw);
+  QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+  bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
+  bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
+  int rowOfDate(const QDate &date) const;
+  bool dateExists(const QDate &date) const;
+  int rowForNewDate(const QDate &date) const;
+  void clear();
+
+  int size() const { return weights.size(); }
+  const WeightList &getWeights() const { return weights; }
+private:
+  void writeToDisk();
+  void readFromDisk();
+
+private:
+  WeightList weights;
+  QFile datafile;
+};
+
+typedef WeightDataModel::DateWeight DW;
+
+#endif // WEIGHTDATA_H
diff --git a/weightgraph/weightgraph.pro b/weightgraph/weightgraph.pro
new file mode 100644 (file)
index 0000000..9a8be0e
--- /dev/null
@@ -0,0 +1,59 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2011-01-16T18:23:01
+#
+#-------------------------------------------------
+
+QT       += core gui maemo5
+
+TARGET = weightgraph
+TEMPLATE = app
+
+
+SOURCES += main.cpp\
+        mainwindow.cpp \
+    weightdata.cpp \
+    editwindow.cpp \
+    weightstatsview.cpp \
+    weightgraphview.cpp \
+    settings.cpp \
+    settingswindow.cpp
+
+HEADERS  += mainwindow.h \
+    weightdata.h \
+    editwindow.h \
+    weightview.h \
+    weightspinbox.h \
+    weightstatsview.h \
+    weightgraphview.h \
+    settings.h \
+    settingswindow.h
+
+FORMS    += mainwindow.ui
+
+CONFIG += mobility console
+MOBILITY = 
+
+RESOURCES += \
+    res.qrc
+
+unix {
+  INSTALLS += target desktop icon64
+  target.path = /usr/bin
+  desktop.path = /usr/share/applications/hildon
+  desktop.files += maemofiles/weightgraph.desktop
+#  icon48.path = /usr/share/icons/hicolor/48x48/apps
+#  icon48.files += maemofiles/48/weightgraph.png
+# icon64.path = /usr/share/icons/hicolor/64x64/apps
+  icon64.path = /usr/share/icons
+  icon64.files += maemofiles/64/weightgraph.png
+#  iconscalable.path = /usr/share/icons/hicolor/scalable/apps
+#  iconscalable.files += maemofiles/64/weightgraph.png
+}
+
+symbian {
+  TARGET.UID3 = 0xecc772d7
+  # TARGET.CAPABILITY +=
+  TARGET.EPOCSTACKSIZE = 0x14000
+  TARGET.EPOCHEAPSIZE = 0x020000 0x800000
+}
diff --git a/weightgraph/weightgraph.pro.user b/weightgraph/weightgraph.pro.user
new file mode 100644 (file)
index 0000000..f435043
--- /dev/null
@@ -0,0 +1,313 @@
+<!DOCTYPE QtCreatorProject>
+<qtcreator>
+ <data>
+  <variable>ProjectExplorer.Project.ActiveTarget</variable>
+  <value type="int">0</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.EditorSettings</variable>
+  <valuemap type="QVariantMap">
+   <value key="EditorConfiguration.Codec" type="QByteArray">System</value>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Target.0</variable>
+  <valuemap type="QVariantMap">
+   <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Maemo</value>
+   <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.Target.MaemoDeviceTarget</value>
+   <value key="ProjectExplorer.Target.ActiveBuildConfiguration" type="int">0</value>
+   <value key="ProjectExplorer.Target.ActiveRunConfiguration" type="int">0</value>
+   <valuemap key="ProjectExplorer.Target.BuildConfiguration.0" type="QVariantMap">
+    <valuemap key="ProjectExplorer.BuildConfiguration.BuildStep.0" type="QVariantMap">
+     <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">qmake</value>
+     <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">QtProjectManager.QMakeBuildStep</value>
+     <valuelist key="QtProjectManager.QMakeBuildStep.QMakeArguments" type="QVariantList"/>
+    </valuemap>
+    <valuemap key="ProjectExplorer.BuildConfiguration.BuildStep.1" type="QVariantMap">
+     <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Make</value>
+     <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.MakeStep</value>
+     <value key="Qt4ProjectManager.MakeStep.Clean" type="bool">false</value>
+     <valuelist key="Qt4ProjectManager.MakeStep.MakeArguments" type="QVariantList"/>
+     <value key="Qt4ProjectManager.MakeStep.MakeCommand" type="QString"></value>
+    </valuemap>
+    <valuemap key="ProjectExplorer.BuildConfiguration.BuildStep.2" type="QVariantMap">
+     <value key="Packaging Enabled" type="bool">true</value>
+     <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString"></value>
+     <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.MaemoPackageCreationStep</value>
+     <valuelist key="Qt4ProjectManager.BuildStep.MaemoPackage.LocalFiles" type="QVariantList"/>
+     <value key="Qt4ProjectManager.BuildStep.MaemoPackage.Modified" type="bool">false</value>
+     <value key="Qt4ProjectManager.BuildStep.MaemoPackage.RemoteExe" type="QString"></value>
+     <valuelist key="Qt4ProjectManager.BuildStep.MaemoPackage.RemoteFiles" type="QVariantList"/>
+     <value key="Version Number" type="QString">1.0.0</value>
+    </valuemap>
+    <value key="ProjectExplorer.BuildConfiguration.BuildStepsCount" type="int">3</value>
+    <valuemap key="ProjectExplorer.BuildConfiguration.CleanStep.0" type="QVariantMap">
+     <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Make</value>
+     <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.MakeStep</value>
+     <value key="Qt4ProjectManager.MakeStep.Clean" type="bool">true</value>
+     <valuelist key="Qt4ProjectManager.MakeStep.MakeArguments" type="QVariantList">
+      <value type="QString">clean</value>
+     </valuelist>
+     <value key="Qt4ProjectManager.MakeStep.MakeCommand" type="QString"></value>
+    </valuemap>
+    <value key="ProjectExplorer.BuildConfiguration.CleanStepsCount" type="int">1</value>
+    <value key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment" type="bool">false</value>
+    <valuelist key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges" type="QVariantList"/>
+    <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Debug</value>
+    <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration" type="int">2</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory" type="QString">/home/visa/koodaus/maemo/WeightGraph-build-maemo</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.QtVersionId" type="int">3</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.ToolChain" type="int">9</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild" type="bool">true</value>
+   </valuemap>
+   <valuemap key="ProjectExplorer.Target.BuildConfiguration.1" type="QVariantMap">
+    <valuemap key="ProjectExplorer.BuildConfiguration.BuildStep.0" type="QVariantMap">
+     <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">qmake</value>
+     <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">QtProjectManager.QMakeBuildStep</value>
+     <valuelist key="QtProjectManager.QMakeBuildStep.QMakeArguments" type="QVariantList"/>
+    </valuemap>
+    <valuemap key="ProjectExplorer.BuildConfiguration.BuildStep.1" type="QVariantMap">
+     <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Make</value>
+     <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.MakeStep</value>
+     <value key="Qt4ProjectManager.MakeStep.Clean" type="bool">false</value>
+     <valuelist key="Qt4ProjectManager.MakeStep.MakeArguments" type="QVariantList"/>
+     <value key="Qt4ProjectManager.MakeStep.MakeCommand" type="QString"></value>
+    </valuemap>
+    <valuemap key="ProjectExplorer.BuildConfiguration.BuildStep.2" type="QVariantMap">
+     <value key="Packaging Enabled" type="bool">true</value>
+     <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString"></value>
+     <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.MaemoPackageCreationStep</value>
+     <valuelist key="Qt4ProjectManager.BuildStep.MaemoPackage.LocalFiles" type="QVariantList"/>
+     <value key="Qt4ProjectManager.BuildStep.MaemoPackage.Modified" type="bool">false</value>
+     <value key="Qt4ProjectManager.BuildStep.MaemoPackage.RemoteExe" type="QString"></value>
+     <valuelist key="Qt4ProjectManager.BuildStep.MaemoPackage.RemoteFiles" type="QVariantList"/>
+     <value key="Version Number" type="QString">0.0.1</value>
+    </valuemap>
+    <value key="ProjectExplorer.BuildConfiguration.BuildStepsCount" type="int">3</value>
+    <valuemap key="ProjectExplorer.BuildConfiguration.CleanStep.0" type="QVariantMap">
+     <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Make</value>
+     <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.MakeStep</value>
+     <value key="Qt4ProjectManager.MakeStep.Clean" type="bool">true</value>
+     <valuelist key="Qt4ProjectManager.MakeStep.MakeArguments" type="QVariantList">
+      <value type="QString">clean</value>
+     </valuelist>
+     <value key="Qt4ProjectManager.MakeStep.MakeCommand" type="QString"></value>
+    </valuemap>
+    <value key="ProjectExplorer.BuildConfiguration.CleanStepsCount" type="int">1</value>
+    <value key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment" type="bool">false</value>
+    <valuelist key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges" type="QVariantList"/>
+    <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Release</value>
+    <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration" type="int">0</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory" type="QString">/home/visa/koodaus/maemo/WeightGraph-build-maemo</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.QtVersionId" type="int">3</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.ToolChain" type="int">9</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild" type="bool">true</value>
+   </valuemap>
+   <value key="ProjectExplorer.Target.BuildConfigurationCount" type="int">2</value>
+   <valuemap key="ProjectExplorer.Target.RunConfiguration.0" type="QVariantMap">
+    <value key=".ProFile" type="QString">WeightGraph.pro</value>
+    <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">New Maemo Run Configuration</value>
+    <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.MaemoRunConfiguration</value>
+    <valuelist key="Qt4ProjectManager.MaemoRunConfiguration.Arguments" type="QVariantList"/>
+    <value key="Qt4ProjectManager.MaemoRunConfiguration.DeviceId" type="qulonglong">3</value>
+    <valuelist key="Qt4ProjectManager.MaemoRunConfiguration.LastDeployedFiles" type="QVariantList">
+     <value type="QString">/home/visa/koodaus/maemo/WeightGraph-build-maemo/weightgraph_0.0.1_armel.deb</value>
+     <value type="QString">192.168.2.15</value>
+     <value type="QString">/home/visa/koodaus/maemo/WeightGraph-build-simulator/weightgraph_0.0.1_armel.deb</value>
+     <value type="QString">/media/puhveli/visa/koodaus/maemo/WeightGraph-build-desktop/weightgraph_0.0.1_armel.deb</value>
+     <value type="QString">/home/visa/koodaus/maemo/WeightGraph-build-maemo/weightgraph_1.0.0_armel.deb</value>
+     <value type="QString">192.168.0.9</value>
+     <value type="QString">/home/visa/koodaus/maemo/WeightGraph-build-maemo/weightgraph_0.0.1_armel.deb</value>
+    </valuelist>
+    <valuelist key="Qt4ProjectManager.MaemoRunConfiguration.LastDeployedHosts" type="QVariantList">
+     <value type="QString">192.168.2.15</value>
+     <value type="QString">/home/visa/koodaus/maemo/WeightGraph-build-maemo/weightgraph_0.0.1_armel.deb</value>
+     <value type="QString">192.168.0.9</value>
+     <value type="QString">192.168.0.9</value>
+     <value type="QString">192.168.0.9</value>
+     <value type="QString">/home/visa/koodaus/maemo/WeightGraph-build-maemo/weightgraph_0.0.1_armel.deb</value>
+     <value type="QString">192.168.0.9</value>
+    </valuelist>
+    <valuelist key="Qt4ProjectManager.MaemoRunConfiguration.LastDeployedRemotePaths" type="QVariantList">
+     <value type="QString">/home/user/weightgraph_0.0.1_armel.deb</value>
+     <value type="QString">/home/user/weightgraph_0.0.1_armel.deb</value>
+     <value type="QString">/home/user/weightgraph_0.0.1_armel.deb</value>
+     <value type="QString">/home/user/weightgraph_0.0.1_armel.deb</value>
+     <value type="QString">/home/user/weightgraph_1.0.0_armel.deb</value>
+     <value type="QString">/home/user/weightgraph_0.0.1_armel.deb</value>
+     <value type="QString">/home/user/weightgraph_0.0.1_armel.deb</value>
+    </valuelist>
+    <valuelist key="Qt4ProjectManager.MaemoRunConfiguration.LastDeployedTimes" type="QVariantList">
+     <value type="QDateTime">2011-01-23T23:49:03</value>
+     <value type="QDateTime">2011-01-20T00:24:31</value>
+     <value type="QDateTime">2011-01-28T21:31:27</value>
+     <value type="QDateTime">2011-01-28T20:41:50</value>
+     <value type="QDateTime">2011-02-23T03:19:59</value>
+     <value type="QDateTime">2011-02-22T01:10:11</value>
+     <value type="QDateTime">2011-02-03T03:44:22</value>
+    </valuelist>
+   </valuemap>
+   <value key="ProjectExplorer.Target.RunConfigurationCount" type="int">1</value>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Target.1</variable>
+  <valuemap type="QVariantMap">
+   <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Qt Simulator</value>
+   <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.Target.QtSimulatorTarget</value>
+   <value key="ProjectExplorer.Target.ActiveBuildConfiguration" type="int">0</value>
+   <value key="ProjectExplorer.Target.ActiveRunConfiguration" type="int">0</value>
+   <valuemap key="ProjectExplorer.Target.BuildConfiguration.0" type="QVariantMap">
+    <valuemap key="ProjectExplorer.BuildConfiguration.BuildStep.0" type="QVariantMap">
+     <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">qmake</value>
+     <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">QtProjectManager.QMakeBuildStep</value>
+     <valuelist key="QtProjectManager.QMakeBuildStep.QMakeArguments" type="QVariantList"/>
+    </valuemap>
+    <valuemap key="ProjectExplorer.BuildConfiguration.BuildStep.1" type="QVariantMap">
+     <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Make</value>
+     <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.MakeStep</value>
+     <value key="Qt4ProjectManager.MakeStep.Clean" type="bool">false</value>
+     <valuelist key="Qt4ProjectManager.MakeStep.MakeArguments" type="QVariantList"/>
+     <value key="Qt4ProjectManager.MakeStep.MakeCommand" type="QString"></value>
+    </valuemap>
+    <value key="ProjectExplorer.BuildConfiguration.BuildStepsCount" type="int">2</value>
+    <valuemap key="ProjectExplorer.BuildConfiguration.CleanStep.0" type="QVariantMap">
+     <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Make</value>
+     <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.MakeStep</value>
+     <value key="Qt4ProjectManager.MakeStep.Clean" type="bool">true</value>
+     <valuelist key="Qt4ProjectManager.MakeStep.MakeArguments" type="QVariantList">
+      <value type="QString">clean</value>
+     </valuelist>
+     <value key="Qt4ProjectManager.MakeStep.MakeCommand" type="QString"></value>
+    </valuemap>
+    <value key="ProjectExplorer.BuildConfiguration.CleanStepsCount" type="int">1</value>
+    <value key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment" type="bool">false</value>
+    <valuelist key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges" type="QVariantList"/>
+    <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Debug</value>
+    <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration" type="int">2</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory" type="QString">/home/visa/koodaus/maemo/WeightGraph-build-simulator</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.QtVersionId" type="int">2</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.ToolChain" type="int">0</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild" type="bool">true</value>
+   </valuemap>
+   <valuemap key="ProjectExplorer.Target.BuildConfiguration.1" type="QVariantMap">
+    <valuemap key="ProjectExplorer.BuildConfiguration.BuildStep.0" type="QVariantMap">
+     <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">qmake</value>
+     <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">QtProjectManager.QMakeBuildStep</value>
+     <valuelist key="QtProjectManager.QMakeBuildStep.QMakeArguments" type="QVariantList"/>
+    </valuemap>
+    <valuemap key="ProjectExplorer.BuildConfiguration.BuildStep.1" type="QVariantMap">
+     <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Make</value>
+     <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.MakeStep</value>
+     <value key="Qt4ProjectManager.MakeStep.Clean" type="bool">false</value>
+     <valuelist key="Qt4ProjectManager.MakeStep.MakeArguments" type="QVariantList"/>
+     <value key="Qt4ProjectManager.MakeStep.MakeCommand" type="QString"></value>
+    </valuemap>
+    <value key="ProjectExplorer.BuildConfiguration.BuildStepsCount" type="int">2</value>
+    <valuemap key="ProjectExplorer.BuildConfiguration.CleanStep.0" type="QVariantMap">
+     <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Make</value>
+     <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.MakeStep</value>
+     <value key="Qt4ProjectManager.MakeStep.Clean" type="bool">true</value>
+     <valuelist key="Qt4ProjectManager.MakeStep.MakeArguments" type="QVariantList">
+      <value type="QString">clean</value>
+     </valuelist>
+     <value key="Qt4ProjectManager.MakeStep.MakeCommand" type="QString"></value>
+    </valuemap>
+    <value key="ProjectExplorer.BuildConfiguration.CleanStepsCount" type="int">1</value>
+    <value key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment" type="bool">false</value>
+    <valuelist key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges" type="QVariantList"/>
+    <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Release</value>
+    <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration" type="int">0</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory" type="QString">/home/visa/koodaus/maemo/WeightGraph-build-simulator</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.QtVersionId" type="int">2</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.ToolChain" type="int">0</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild" type="bool">true</value>
+   </valuemap>
+   <value key="ProjectExplorer.Target.BuildConfigurationCount" type="int">2</value>
+   <valuemap key="ProjectExplorer.Target.RunConfiguration.0" type="QVariantMap">
+    <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">weightgraph</value>
+    <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.Qt4RunConfiguration</value>
+    <value key="Qt4ProjectManager.Qt4RunConfiguration.BaseEnvironmentBase" type="int">2</value>
+    <valuelist key="Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments" type="QVariantList"/>
+    <value key="Qt4ProjectManager.Qt4RunConfiguration.ProFile" type="QString">weightgraph.pro</value>
+    <value key="Qt4ProjectManager.Qt4RunConfiguration.UseDyldImageSuffix" type="bool">false</value>
+    <value key="Qt4ProjectManager.Qt4RunConfiguration.UseTerminal" type="bool">false</value>
+    <valuelist key="Qt4ProjectManager.Qt4RunConfiguration.UserEnvironmentChanges" type="QVariantList"/>
+    <value key="Qt4ProjectManager.Qt4RunConfiguration.UserSetName" type="bool">false</value>
+    <value key="Qt4ProjectManager.Qt4RunConfiguration.UserSetWorkingDirectory" type="bool">false</value>
+    <value key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory" type="QString"></value>
+   </valuemap>
+   <value key="ProjectExplorer.Target.RunConfigurationCount" type="int">1</value>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Target.2</variable>
+  <valuemap type="QVariantMap">
+   <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Desktop</value>
+   <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.Target.DesktopTarget</value>
+   <value key="ProjectExplorer.Target.ActiveBuildConfiguration" type="int">0</value>
+   <value key="ProjectExplorer.Target.ActiveRunConfiguration" type="int">0</value>
+   <valuemap key="ProjectExplorer.Target.BuildConfiguration.0" type="QVariantMap">
+    <valuemap key="ProjectExplorer.BuildConfiguration.BuildStep.0" type="QVariantMap">
+     <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">qmake</value>
+     <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">QtProjectManager.QMakeBuildStep</value>
+     <valuelist key="QtProjectManager.QMakeBuildStep.QMakeArguments" type="QVariantList"/>
+    </valuemap>
+    <valuemap key="ProjectExplorer.BuildConfiguration.BuildStep.1" type="QVariantMap">
+     <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Make</value>
+     <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.MakeStep</value>
+     <value key="Qt4ProjectManager.MakeStep.Clean" type="bool">false</value>
+     <valuelist key="Qt4ProjectManager.MakeStep.MakeArguments" type="QVariantList"/>
+     <value key="Qt4ProjectManager.MakeStep.MakeCommand" type="QString"></value>
+    </valuemap>
+    <value key="ProjectExplorer.BuildConfiguration.BuildStepsCount" type="int">2</value>
+    <valuemap key="ProjectExplorer.BuildConfiguration.CleanStep.0" type="QVariantMap">
+     <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Make</value>
+     <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.MakeStep</value>
+     <value key="Qt4ProjectManager.MakeStep.Clean" type="bool">true</value>
+     <valuelist key="Qt4ProjectManager.MakeStep.MakeArguments" type="QVariantList">
+      <value type="QString">clean</value>
+     </valuelist>
+     <value key="Qt4ProjectManager.MakeStep.MakeCommand" type="QString"></value>
+    </valuemap>
+    <value key="ProjectExplorer.BuildConfiguration.CleanStepsCount" type="int">1</value>
+    <value key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment" type="bool">false</value>
+    <valuelist key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges" type="QVariantList"/>
+    <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Debug</value>
+    <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration" type="int">2</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory" type="QString">/media/puhveli/visa/koodaus/maemo/WeightGraph-build-desktop</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.QtVersionId" type="int">8</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.ToolChain" type="int">0</value>
+    <value key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild" type="bool">true</value>
+   </valuemap>
+   <value key="ProjectExplorer.Target.BuildConfigurationCount" type="int">1</value>
+   <valuemap key="ProjectExplorer.Target.RunConfiguration.0" type="QVariantMap">
+    <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">weightgraph</value>
+    <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.Qt4RunConfiguration</value>
+    <value key="Qt4ProjectManager.Qt4RunConfiguration.BaseEnvironmentBase" type="int">2</value>
+    <valuelist key="Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments" type="QVariantList"/>
+    <value key="Qt4ProjectManager.Qt4RunConfiguration.ProFile" type="QString">weightgraph.pro</value>
+    <value key="Qt4ProjectManager.Qt4RunConfiguration.UseDyldImageSuffix" type="bool">false</value>
+    <value key="Qt4ProjectManager.Qt4RunConfiguration.UseTerminal" type="bool">false</value>
+    <valuelist key="Qt4ProjectManager.Qt4RunConfiguration.UserEnvironmentChanges" type="QVariantList"/>
+    <value key="Qt4ProjectManager.Qt4RunConfiguration.UserSetName" type="bool">false</value>
+    <value key="Qt4ProjectManager.Qt4RunConfiguration.UserSetWorkingDirectory" type="bool">false</value>
+    <value key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory" type="QString"></value>
+   </valuemap>
+   <value key="ProjectExplorer.Target.RunConfigurationCount" type="int">1</value>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.TargetCount</variable>
+  <value type="int">3</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Updater.FileVersion</variable>
+  <value type="int">4</value>
+ </data>
+</qtcreator>
diff --git a/weightgraph/weightgraphview.cpp b/weightgraph/weightgraphview.cpp
new file mode 100644 (file)
index 0000000..a9d93e1
--- /dev/null
@@ -0,0 +1,272 @@
+#include "weightgraphview.h"
+#include "settings.h"
+#include <QPainter>
+#include <QDebug>
+#include <QTimer>
+#include <cmath>
+#include <QtGui/QX11Info>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+WeightGraphView::WeightGraphView(WeightDataModel *wdm,
+                                 const QString &id, QWidget *parent) :
+    QWidget(parent), id(id), wdm(wdm),
+    period(Settings::graphSettings(id).defaultTimeInterval)
+{
+  setWindowTitle("WeightGraph");
+
+  setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+
+  connect(wdm, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+          this, SLOT(update()));
+  connect(wdm, SIGNAL(rowsInserted(const QModelIndex&,int,int)),
+          this, SLOT(update()));
+  connect(wdm, SIGNAL(rowsRemoved(const QModelIndex&,int,int)),
+          this, SLOT(update()));
+  connect(Settings::self(), SIGNAL(settingChanged()),
+          this, SLOT(update()));
+}
+
+void WeightGraphView::mousePressEvent(QMouseEvent *)
+{
+  emit clicked();
+}
+
+QSize WeightGraphView::sizeHint() const
+{
+  return QSize(300, 400);
+}
+
+void WeightGraphView::keyPressEvent(QKeyEvent* event)
+{
+  //qDebug() << "Key pressed: " << event->key();
+  switch (event->key()) {
+  case Qt::Key_F7:
+      decPeriod();
+      event->accept();
+      break;
+
+  case Qt::Key_F8:
+      incPeriod();
+      event->accept();
+      break;
+  }
+  QWidget::keyPressEvent(event);
+}
+
+// Macros to transform dates and weights to paintdevice coords
+#define D_X(d) ((d)*qreal(width())/days)
+#define DW_X(dw) D_X(f.daysTo((dw).date))
+#define W_Y(w) (height()*(maxW-(w))/weightInterval)
+#define DW_Y(dw) W_Y((dw).weight)
+#define DW_POINT(dw) QPointF(DW_X(dw), DW_Y(dw))
+
+inline double weightIntervalToMult(double wi)
+{
+  if (wi <= 0.2) return 0.01;
+  else if (wi <= 0.5) return 0.025;
+  else if (wi <= 1.0) return 0.05;
+  else if (wi <= 2.0) return 0.1;
+  else if (wi <= 5.0) return 0.25;
+  else if (wi <= 10.0) return 0.5;
+  else if (wi <= 20.0) return 1.0;
+  else if (wi <= 50.0) return 2.5;
+  else if (wi <= 100.0) return 5.0;
+  else if (wi <= 200.0) return 10.0;
+  else if (wi <= 500.0) return 25.0;
+  else if (wi <= 1000.0) return 50.0;
+  else return 100.0;
+}
+
+void WeightGraphView::paintEvent(QPaintEvent *)
+{
+  QPainter painter(this);
+  painter.setRenderHint(QPainter::Antialiasing);
+
+  GraphSettings gs = Settings::graphSettings(id);
+  const QList<DW> &allWeights = wdm->getWeights();
+
+  double min, max;
+  QList<DW> weights;
+  const DW *beforeFirst = NULL;
+  if (allWeights.size() < 2) {
+    min = 0;
+    max = 100;
+  }
+  else {
+    bool firstFound = false;
+    min = 1e30;
+    max = -1e30;
+    const QDate &l = allWeights.last().date;
+    for(int i=0; i < allWeights.size(); i++) {
+      if (period == 0 || firstFound || allWeights[i].date.daysTo(l) <= period) {
+        weights.append(allWeights[i]);
+        if (allWeights[i].weight < min)
+          min = allWeights[i].weight;
+        if (allWeights[i].weight > max)
+          max = allWeights[i].weight;
+        firstFound = true;
+      }
+      if (!firstFound)
+        beforeFirst = &allWeights[i];
+    }
+  }
+
+  if (gs.weightIntervalMode == GraphSettings::AutomaticWithGoalWeight
+      && gs.goalWeightEnabled) {
+    min = qMin(min, Settings::goalWeightMin());
+    max = qMax(max, Settings::goalWeightMax());
+  }
+  else if(gs.weightIntervalMode == GraphSettings::Manual) {
+    if (gs.weightIntervalMax > gs.weightIntervalMin) {
+      min = gs.weightIntervalMin;
+      max = gs.weightIntervalMax;
+    }
+  }
+  // else default is min and max of actual data
+
+  double margin = (max - min)*0.03;
+  double minW = min - margin;
+  double maxW = max + margin;
+  double weightInterval = maxW-minW;
+  if (maxW-minW < 0.1) {
+    minW = min-0.003;
+    maxW = max+0.003;
+    weightInterval = maxW - minW;
+  }
+  QDate f, l;
+  int days;
+  if (weights.size() < 2) {
+    l = QDate::currentDate();
+    f = l.addDays(-7);
+    days = 7;
+  }
+  else if (period==0) {
+    f = weights.first().date;
+    l = weights.last().date;
+    days = f.daysTo(l);
+  } else {
+    l = weights.last().date;
+    f = l.addDays(-period);
+    days = period;
+  }
+
+  // Interpolate to fill gap in left part of the graph
+  if (weights.first().date != f && beforeFirst != NULL) {
+    DW dw;
+    dw.date = f;
+    dw.weight = (weights.first().weight - beforeFirst->weight)
+                /beforeFirst->date.daysTo(weights.first().date)
+                *beforeFirst->date.daysTo(f)
+                +beforeFirst->weight;
+    weights.prepend(dw);
+  }
+
+  painter.setWindow(-50, 0, width()+85, height()+25);
+
+  if (gs.goalWeightEnabled
+      && ((Settings::goalWeightMin() > minW && Settings::goalWeightMin() < maxW)
+          ||(Settings::goalWeightMax() > minW && Settings::goalWeightMax() < maxW))) {
+    QPen oldPen = painter.pen();
+    QBrush oldBrush = painter.brush();
+    painter.setPen(Qt::NoPen);
+    painter.setBrush(QColor(0,255,0,100));
+    painter.setClipRect(QRectF(0,0,width(),height()));
+    painter.drawRect(QRectF(QPointF(0, W_Y(Settings::goalWeightMax())),
+                            QPointF(width(), W_Y(Settings::goalWeightMin()))));
+    painter.setClipping(false);
+    painter.setPen(oldPen);
+    painter.setBrush(oldBrush);
+  }
+
+  // Y-axis
+  QFont font = painter.font();
+  font.setPixelSize(16);
+  painter.setFont(font);
+  painter.drawLine(QPointF(0.0,0.0), QPointF(0,height()));
+  double mult = weightIntervalToMult(weightInterval);
+  int count = 0;
+  for(double w=ceil(minW/mult)*mult; w < maxW; w += mult, count++) {
+    double len = count%5==0 ? 7.0 : 4.0;
+    painter.drawLine(QPointF(-len,W_Y(w)),QPointF(len,W_Y(w)));
+    if (count%5 == 0) {
+      QPen p = painter.pen();
+      painter.setPen(QColor(50,50,50));
+      painter.drawLine(QPointF(len,W_Y(w)),QPointF(width(),W_Y(w)));
+      painter.setPen(p);
+      QString text = tr("%1").arg(double(w),0,'f', mult <= 0.25 ? 2 : 1);
+      QSize textSize = painter.fontMetrics().size(0, text);
+      painter.drawText(QPointF(-len-3-textSize.width(), W_Y(w)+6), text);
+    }
+  }
+
+  // X-axis
+  font.setPixelSize(13);
+  painter.setFont(font);
+  double endOfLastDate = -1e6;
+  mult = days/30+1;
+  painter.drawLine(QPointF(0.0,height()), QPointF(width(),height()));
+  for(int day=0; day <= days; day+=mult) {
+    QString text = f.addDays(day).toString(Qt::ISODate);
+    QSize textSize = painter.fontMetrics().size(0, text);
+    double tickLen;
+    if (D_X(day)-textSize.width()/2 > endOfLastDate + 10) {
+      tickLen = 5.0;
+      painter.drawText(QPointF(D_X(day)-textSize.width()/2,
+                               W_Y(minW)+18), text);
+      endOfLastDate = D_X(day)+textSize.width()/2;
+    }
+    else
+      tickLen = 3.0;
+    painter.drawLine(QPointF(D_X(day), W_Y(minW)-tickLen),
+                     QPointF(D_X(day), W_Y(minW)+tickLen));
+  }
+
+  // The weight data
+  QPolygonF linepoints;
+  foreach(DW dw, weights) {
+    linepoints << DW_POINT(dw);
+  }
+  painter.drawPolyline(linepoints);
+
+}
+void WeightGraphView::show()
+{
+  QWidget::show();
+  grabZoomKeys(Settings::grabZoomKeys()); //Need to be regrabbed somewhy
+  //Work around a bug: hidden graphs don't update. Must wait for the
+  //graph to actually show up, then call update.
+  QTimer *tmp = new QTimer(this);
+  tmp->setSingleShot(true);
+  tmp->setInterval(500);
+  connect(tmp, SIGNAL(timeout()), this, SLOT(update()));
+  tmp->start();
+}
+
+void WeightGraphView::grabZoomKeys(bool grab)
+{
+  if (!winId()) {
+    qWarning("Can't grab keys unless we have a window id");
+    return;
+  }
+
+  unsigned long val = (grab) ? 1 : 0;
+  Atom atom = XInternAtom(QX11Info::display(), "_HILDON_ZOOM_KEY_ATOM", False);
+  if (!atom) {
+    qWarning("Unable to obtain _HILDON_ZOOM_KEY_ATOM. This example will only work "
+             "on a Maemo 5 device!");
+    return;
+  }
+
+
+  XChangeProperty (QX11Info::display(),
+                   winId(),
+                   atom,
+                   XA_INTEGER,
+                   32,
+                   PropModeReplace,
+                   reinterpret_cast<unsigned char *>(&val),
+                   1);
+
+  //qDebug() << "Grabbed for winId " << winId();
+}
diff --git a/weightgraph/weightgraphview.h b/weightgraph/weightgraphview.h
new file mode 100644 (file)
index 0000000..835b735
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef WEIGHTGRAPHVIEW_H
+#define WEIGHTGRAPHVIEW_H
+
+#include <QWidget>
+#include <QKeyEvent>
+#include "weightdata.h"
+#include "settings.h"
+
+class WeightGraphView : public QWidget
+{
+    Q_OBJECT
+public:
+  explicit WeightGraphView(WeightDataModel *wdm, const QString &id, QWidget *parent = 0);
+  QSize sizeHint() const;
+signals:
+  void clicked();
+public slots:
+  void paintEvent(QPaintEvent *);
+  void show();
+  void update() {
+    grabZoomKeys(Settings::grabZoomKeys());
+    QWidget::update();
+  }
+
+  void grabZoomKeys(bool grab);
+  void incPeriod() {
+    if (period == 0)
+      period = wdm->getWeights().first().date.daysTo(wdm->getWeights().last().date) + 1;
+    else
+      period++;
+    update();
+  }
+  void decPeriod() {
+    if (period == 0)
+      period = wdm->getWeights().first().date.daysTo(wdm->getWeights().last().date) - 1;
+    else
+      period = qMax(1, period-1);
+    update();
+  }
+  void setPeriod(int period) { this->period = period; update(); }
+protected:
+  void mousePressEvent(QMouseEvent *);
+  void keyPressEvent(QKeyEvent* event);
+private:
+  QString id;
+  WeightDataModel *wdm;
+  int period;
+};
+
+#endif // WEIGHTGRAPHVIEW_H
diff --git a/weightgraph/weightspinbox.h b/weightgraph/weightspinbox.h
new file mode 100644 (file)
index 0000000..626ef7c
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef WEIGHTSPINBOX_H
+#define WEIGHTSPINBOX_H
+
+#include <QDoubleSpinBox>
+#include "settings.h"
+
+class WeightSpinBox : public QDoubleSpinBox
+{
+  Q_OBJECT
+public:
+  WeightSpinBox(QWidget *parent=0) : QDoubleSpinBox(parent)
+  {
+    setDecimals(1);
+    setSingleStep(0.1);
+    setMinimum(10.0);
+    setMaximum(999.9);
+    setValue(60.0);
+    updateWeightUnit();
+    connect(Settings::self(), SIGNAL(settingChanged()),
+            this, SLOT(updateWeightUnit()));
+  }
+private slots:
+  void updateWeightUnit() {
+    setSuffix(" "+Settings::weightUnit());
+  }
+};
+
+#endif // WEIGHTSPINBOX_H
diff --git a/weightgraph/weightstatsview.cpp b/weightgraph/weightstatsview.cpp
new file mode 100644 (file)
index 0000000..65e4f0f
--- /dev/null
@@ -0,0 +1,77 @@
+#include "weightstatsview.h"
+#include "settings.h"
+#include <QVBoxLayout>
+#include <QGroupBox>
+
+#include <QDebug>
+
+WeightStatsView::WeightStatsView(WeightDataModel *wdm, QWidget *parent) :
+  QWidget(parent), wdm(wdm)
+{
+  connect(wdm, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+          this, SLOT(updateStats()));
+  connect(wdm, SIGNAL(rowsInserted(const QModelIndex&,int,int)),
+          this, SLOT(updateStats()));
+  connect(wdm, SIGNAL(rowsRemoved(const QModelIndex&,int,int)),
+          this, SLOT(updateStats()));
+  connect(Settings::self(), SIGNAL(settingChanged()),
+          this, SLOT(updateStats()));
+
+  QVBoxLayout *lo = new QVBoxLayout(this);
+
+  last = new QLabel(this);
+  lo->addWidget(last);
+
+  change = new QLabel(this);
+  lo->addWidget(change);
+
+  updateStats();
+}
+
+QString dateString(QDate date)
+{
+  int days = date.daysTo(QDate::currentDate());
+  if (days < 0)
+    return date.toString(Qt::ISODate);
+  else if (days == 0)
+    return "Today";
+  else if (days == 1)
+    return "Yesterday";
+  else if (days < 7)
+    return date.toString("dddd");
+  else
+    return date.toString(Qt::ISODate);
+}
+
+QString dateIntervalString(int days)
+{
+  if (days < 30)
+    return QString("%1 days").arg(days);
+  else if (days < 360)
+    return QString("%1m %2d").arg(days/30).arg(days%30);
+  else if (days < 365)
+    return QString("1y 0m");
+  else
+    return QString("%1y %2m").arg(days/365).arg((days%365)/30);
+}
+
+void WeightStatsView::updateStats()
+{
+  if (wdm->size() == 0) {
+    last->setText("Last: No data");
+    change->setText("Change: No data");
+    return;
+  }
+  QString unit = Settings::weightUnit();
+  const DW &f = wdm->getWeights().first();
+  const DW &l = wdm->getWeights().last();
+  last->setText(tr("Last: %1 %2\n     (%3)")
+                .arg(l.weight,0,'f',1)
+                .arg(unit)
+                .arg(dateString(l.date)));
+  change->setText(tr("Change: %1 %2\n"
+                     "     (in %3)")
+                  .arg(l.weight-f.weight,0,'f',1)
+                  .arg(unit)
+                  .arg(dateIntervalString(f.date.daysTo(l.date))));
+}
diff --git a/weightgraph/weightstatsview.h b/weightgraph/weightstatsview.h
new file mode 100644 (file)
index 0000000..ac4c834
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef WEIGHTSTATSVIEW_H
+#define WEIGHTSTATSVIEW_H
+
+#include <QWidget>
+#include <QLabel>
+#include "weightdata.h"
+
+class WeightStatsView : public QWidget
+{
+  Q_OBJECT
+public:
+  explicit WeightStatsView(WeightDataModel *wdm, QWidget *parent = 0);
+
+
+signals:
+
+public slots:
+  void updateStats();
+private:
+  WeightDataModel *wdm;
+  QLabel *last;
+  QLabel *change;
+};
+
+#endif // WEIGHTSTATSVIEW_H
diff --git a/weightgraph/weightview.h b/weightgraph/weightview.h
new file mode 100644 (file)
index 0000000..7234b36
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef WEIGHTVIEW_H
+#define WEIGHTVIEW_H
+
+#include <QTableView>
+#include <QStyledItemDelegate>
+#include <QItemEditorCreatorBase>
+#include <QDoubleSpinBox>
+#include <QItemEditorFactory>
+#include <QFont>
+#include <QHeaderView>
+#include "weightspinbox.h"
+
+class WeightView : public QTableView
+{
+  Q_OBJECT
+public:
+  explicit WeightView(QWidget *parent = 0) :
+        QTableView(parent)
+  {
+    QItemEditorFactory *editorFactory = new QItemEditorFactory;
+    editorFactory->registerEditor(QVariant::Double, new WeightView::WeightEditCreator);
+    delegate.setItemEditorFactory(editorFactory);
+    setItemDelegate(&delegate);
+    setSelectionMode(QAbstractItemView::SingleSelection);
+    this->verticalHeader()->hide();
+    this->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
+    this->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
+    QFont f;
+    f.setPointSize(28);
+    setFont(f);
+  }
+
+  class WeightEditCreator : public QItemEditorCreatorBase
+  {
+  public:
+    WeightEditCreator() : QItemEditorCreatorBase() { }
+    virtual QWidget *createWidget(QWidget *parent) const
+    {
+      return new WeightSpinBox(parent);
+    }
+    virtual QByteArray valuePropertyName() const { return "value"; }
+  };
+  private:
+    QStyledItemDelegate delegate;
+};
+
+#endif // WEIGHTVIEW_H
diff --git a/welcome b/welcome
deleted file mode 100644 (file)
index e69de29..0000000
index 7dadd5d..8e2216a 100644 (file)
@@ -7,9 +7,10 @@
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 </head>
 <body>
-WeightGraph is a Maemo 5 utility that helps you keep track of your weight. It
+<h1>WeightGraph</h1>
+<p>WeightGraph is a Maemo 5 utility that helps you keep track of your weight. It
 lets you record weight measurements daily and as the name suggests, it plots a
 nice curve based on the weight data. WeightGraph is designed to be simple and
-easy to use.
+easy to use.</p>
 </body>
 </html>