--- /dev/null
+#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;
+ }
+}