test
[push-it] / src / mainwindow.cpp
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
new file mode 100644 (file)
index 0000000..84ebb31
--- /dev/null
@@ -0,0 +1,683 @@
+/*
+    Copyright (C) <2010>  <Markus Scharnowski markus.scharnowski@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QDataStream>
+#include <QApplication>
+#include <QLayout>
+#include <QInputDialog>
+#include <QTextCodec>
+
+#include <assert.h>
+#include <sstream>
+#include <iomanip>
+#include <iostream>
+
+#include "mainwindow.h"
+#include "aboutdialog.hpp"
+#include "showfulllistdialog.hpp"
+
+#define DEBUG_OUTPUT
+#undef DEBUG_OUTPUT
+
+MainWindow::MainWindow(QWidget *parent) :
+    QMainWindow(parent)
+{
+  snprintf(mainButtonStartText,BUFSIZ-1,
+           "&Start\n\n"
+           "If this program is used uncautiously\n"
+           "you could break your device physically.\n"
+           "Always be sure that you use this\n"
+           "software and the corresponding\n"
+           "hardware in a way that the hardware\n"
+           "won't get damaged."
+           );
+  this->setupUi();
+  fsExitButton = new FullScreenExitButton(this);
+  fsExitButton->hide();
+
+  timer = new QTimer(this);
+  connect(timer, SIGNAL(timeout()), this, SLOT(mainButtonTimeoutUpdate()));
+  timerRefreshTimeMs = 100;
+
+  connect(actionReset, SIGNAL(triggered()), this , SLOT(resetProgram()));
+  connect(actionResetFs, SIGNAL(triggered()), this , SLOT(resetProgramAndFullscreen()));
+//  connect(actionResetFs, SIGNAL(triggered()), this , SLOT(actionReset->Trigger));
+  connect(actionSimulated_click, SIGNAL(triggered()), this , SLOT(mainButtonRefresh()));
+  connect(actionPause, SIGNAL(triggered()), this , SLOT(pauseCounting()));
+  connect(actionSave_full_list, SIGNAL(triggered()), this , SLOT(saveToFile()));
+  connect(actionEnable_full_date, SIGNAL(triggered()), this , SLOT(toggleFullDate()));
+  connect(actionEnable_time_and_date, SIGNAL(triggered()), this , SLOT(toggleTimeDate()));
+  connect(actionAbout, SIGNAL(triggered()), this , SLOT(about()));
+  connect(actionShow_full_list, SIGNAL(triggered()), this , SLOT(showFullList()));
+  connect(action_Fullscreen, SIGNAL(triggered()), this, SLOT(showFullScreenHandler()));
+  connect(actionChangeNumberOfLines, SIGNAL(triggered()), this, SLOT(changeNumberOfLines()));
+  connect(actionChangeFontSize, SIGNAL(triggered()), this, SLOT(changeFontSize()));
+
+  connect(this->mainButton, SIGNAL(clicked()), this, SLOT(mainButtonRefresh()));
+
+  changeNumberOfLines(8);
+  changeFontSize(30);
+  resetProgram();
+}
+
+MainWindow::~MainWindow()
+{
+}
+
+int MainWindow::updateTime()
+{
+  hitCount++;
+  if(1 == hitCount)
+  {
+    overallTime.start();
+  }
+  overallTime.update();
+
+  return EXIT_SUCCESS;
+}
+
+void MainWindow::mainButtonRefresh()
+{
+  updateTime();
+  QString latest = generateHistoryString();
+  QString string = completeString.remove(mainButtonStartText);
+  QStringList lines;
+  QString header = getHeaderLine();
+
+  /* timer handling */
+  if(NULL != timer)
+  {
+    if(1 == hitCount)
+    {
+      timer->start(timerRefreshTimeMs);
+    }
+    else if(hitCount > 1)
+    {
+      if(!timer->isActive())
+      {
+        timer->start(timerRefreshTimeMs);
+      }
+    }
+  }
+
+  if(1 == hitCount)
+  {
+    mainButtonRefreshTextOnly();
+    return;
+  }
+  lines = string.split("\n");
+
+  /* add new entry information directly behind the current header
+       wihtout start information if there is any.
+    */
+  if(lines.length() > 0)
+  {
+    if(lines[0].indexOf("#0") > 0)
+    {
+      lines[0].truncate(lines[0].indexOf("#0"));
+    }
+    lines[0].append(latest);
+  }
+
+  string = lines.join("\n").prepend("\n").prepend(header);
+  completeString = string;
+  lines = string.split("\n");
+  /* adjust the total number of lines of the button */
+  if(lines.length() > maximumNumberOfLines)
+  {
+    while(lines.length() > maximumNumberOfLines
+          && lines.length() > 0)
+    {
+      lines.removeLast();
+    }
+  }
+  else if(lines.length() < maximumNumberOfLines)
+  {
+    while(lines.length() < maximumNumberOfLines)
+    {
+      lines.append(" ");
+    }
+  }
+  string = lines.join("\n");
+  this->mainButton->setText(tr(string.toStdString().c_str()));
+  toggleActionPause();
+#ifdef DEBUG_OUTPUT
+  std::cout << "---" << std::endl << string.toStdString() << std::endl << "-" << string.split("\n").length() << "-" << std::endl;
+#endif
+}
+
+QString MainWindow::generateHistoryString()
+{
+  QString result;
+  char tmp[BUFSIZ];
+  std::string previousStr, lastStr, firstStr;
+
+  previousStr = overallTime.getPreviousTimeString();
+  lastStr = overallTime.getLastTimeString();
+  firstStr = overallTime.getStartTimeString();
+
+   snprintf(tmp,BUFSIZ-1,
+      "\n"
+//      "Δ Delta %.3lfs "
+//      "⅀∑ Total %.3lfs "
+      "Δ %.3lfs "
+      "∑ %.3lfs "
+      "Avg %.3lfs"
+      ,
+      overallTime.getDeltaLastToPrev(),
+      overallTime.getDeltaLastToStart(),
+      overallTime.getDeltaLastToStart()/(hitCount-1)
+      );
+
+  result = tr(tmp);
+
+  if(true == timeOutput)
+  {
+    result.prepend(lastStr.c_str());
+  }
+
+  return result;
+}
+
+int MainWindow::resetCounters()
+{
+  hitCount=-1;
+  updateTime();
+  return EXIT_SUCCESS;
+}
+
+int MainWindow::resetUi()
+{
+  this->mainButton->setText(tr(mainButtonStartText));
+  this->actionPause->setText(tr("&Pause"));
+  this->actionEnable_full_date->setDisabled(true);
+
+  completeString = this->mainButton->text();
+  timeOutput = false;
+
+  return EXIT_SUCCESS;
+}
+
+void MainWindow::resetProgram()
+{
+  resetCounters();
+  resetUi();
+  if(timer->isActive())
+  {
+    timer->stop();
+  }
+}
+
+void MainWindow::pauseCounting()
+{
+  overallTime.pause();
+  toggleTimer();
+  toggleActionPause();
+}
+
+int MainWindow::toggleTimer()
+{
+  if(timer->isActive())
+  {
+    timer->stop();;
+  }
+  else
+  {
+    timer->start(timerRefreshTimeMs);
+  }
+  return EXIT_SUCCESS;
+}
+
+void MainWindow::mainButtonTimeoutUpdate()
+{
+  if(!timer->isActive())
+  {
+    assert(0 && "Unintended use");
+    return;
+  }
+  else if(overallTime.isPaused())
+  {
+    timer->stop();
+    return;
+  }
+
+  QString string = this->mainButton->text();
+  QStringList lines = string.split("\n");
+  if(lines.length() >= 1)
+  {
+    lines[0] = getHeaderLine();
+  }
+  else
+  {
+    assert(0 && "Lines are missing");
+  }
+  this->mainButton->setText(tr(lines.join("\n").toStdString().c_str()));
+
+  //update stored string
+  lines = completeString.split("\n");
+  if(lines.length() >= 1)
+  {
+    lines[0] = getHeaderLine();
+  }
+  completeString = lines.join("\n");
+}
+
+QString MainWindow::getHeaderLine()
+{
+  QString headerLine;
+  std::stringstream sstr;
+
+  double runTime = overallTime.getDeltaNowToStart();
+  double time = 0;
+  int hours = 0;
+  int min = 0;
+
+  sstr << "#" << hitCount << " ";
+  sstr << std::setfill('0');
+  hours = (int)(runTime/3600);
+  sstr << hours << ":"; //hour
+  time = runTime - 3600*hours;
+  min = (int)(time/60);
+  sstr << std::setw(2) << min << ":"; //minute
+  time = runTime - 3600*hours - 60*min;
+  sstr.setf(std::ios::fixed);
+  sstr.setf(std::ios::showpoint);
+  sstr.precision(1);
+  sstr << time << " "; //seconds
+  if(true == timeOutput)
+  {
+    sstr << "#0 " << overallTime.getStartTimeString();
+  }
+
+  headerLine = sstr.str().c_str();
+  return headerLine;
+}
+
+void MainWindow::saveToFile()
+{
+  ShowFullListDialog *listHander;
+  listHander = new ShowFullListDialog(0,completeString);
+  listHander->save();
+}
+
+int MainWindow::toggleActionPause()
+{
+  QString str = this->actionPause->text();
+  if(str == "&Pause" && overallTime.isPaused())
+  {
+    this->actionPause->setText(tr("R&esume"));
+  }
+  else
+  {
+    this->actionPause->setText(tr("&Pause"));
+  }
+  return EXIT_SUCCESS;
+}
+
+int MainWindow::updateUiText()
+{
+  toggleActionPause();
+  return EXIT_SUCCESS;
+}
+
+void MainWindow::toggleFullDate()
+{
+  if(overallTime.isDateForStringsEnabled())
+  {
+    overallTime.disableDateForStrings();
+  }
+  else
+  {
+    overallTime.enableDateForStrings();
+  }
+}
+
+void MainWindow::toggleTimeDate()
+{
+  if(this->actionEnable_time_and_date->isChecked())
+  {
+    this->actionEnable_full_date->setEnabled(true);
+    timeOutput = true;
+  }
+  else
+  {
+    this->actionEnable_full_date->setDisabled(true);
+    timeOutput = false;
+  }
+}
+
+void MainWindow::about()
+{
+  AboutDialog *pa = new AboutDialog;
+  QTextBrowser *examples = new QTextBrowser;
+  examples->setOpenExternalLinks(true);
+  examples->setHtml(
+      "<p><b><a href =\"http://sites.google.com/site/markusscharnowski/n/pushit/real-life-examples\">Examples website with videos and pictures.</a></b><p>"
+
+      "<p>Excerpt:</p>"
+
+      "<p><b>Pull ups</b></p>"
+      "<p>Place the phone add the position where you are in the highest position, so you can hit it with your nose. In my case I mount the Nokia N900 above a door with the help of some tesa-strips. I also tried it without tesa-strips but after some pushes on the N900 I was too anxious that it could fall down on the floor. So I recommend some safety mechanism to have fun with your device now and in the future. Every time you pulling yourself up, you touch the screen of the N900 with your nose. So you will always know how many pull ups you have done.</p>"
+
+      "<p><b>Push ups</b></p>"
+      "<p>Place the smartphone just below your nose so you can hit it with the nose when in the lowest position. And then start pushing up.</p>"
+
+      "<p><b>Crunches</b></p>"
+      "Doing crunches is a tiny bit more tricky. You have to go into your crunching position (laying on your back, feet up) and place your phone behind your head. Positioned well, you will be able to touch your phone with one of your thumbs every time you are going down (assuming you fold your hands behind your head when doing crunches)."
+
+      "<p><b>Lower back exercise</b></p>"
+      "<p>Place the phone just below your nose so you can hit it with the nose. I don't know how this exercise is called - if you know it, please let me know it too.<br>"
+      "To execute this exercise I lay myself on some pillows on a bench. My legs will get support by a table. The exercise itself is moving you upper body down to the phone and up again till your back is strait like when your are standing on your feet.</p>"
+      );
+
+  pa->setProgramName(windowTitle());
+  pa->setProgramUrl("http://sites.google.com/site/markusscharnowski/n/pushit");
+  pa->tabs->insertTab(5,examples,tr("&Examples"));
+  pa->setMinimumHeight(400);
+
+  pa->show();
+}
+
+void MainWindow::showFullList()
+{
+  ShowFullListDialog *ps = new ShowFullListDialog(0,completeString);
+  ps->show();
+}
+
+void MainWindow::showFullScreenHandler()
+{
+  bool isFullScreen = windowState() & Qt::WindowFullScreen;
+  if (isFullScreen)
+  {
+    showNormal();
+//    this->action_Fullscreen->setText(tr("&Fullscreen"));
+  }
+  else
+  {
+    showFullScreen();
+    fsExitButton->show();
+//    this->action_Fullscreen->setText(tr("&Disable Fullscreen"));
+  }
+}
+
+void MainWindow::slotFullscreenExit()
+{
+  action_Fullscreen->trigger();
+}
+
+void MainWindow::setupUi()
+{
+  if (this->objectName().isEmpty())
+  {
+    this->setObjectName(QString::fromUtf8("MainWindow"));
+  }
+  setBaseSize(800,480);
+  resize(800,480);
+
+  /* main button */
+  mainButton = new QPushButton;
+  mainButton->setObjectName(QString::fromUtf8("mainButton"));
+  QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+  mainButton->setSizePolicy(sizePolicy);
+
+  QFont font1;
+  font1.setPointSize(26);
+  font1.setBold(true);
+  font1.setWeight(75);
+
+  mainButton->setFont(font1);
+  mainButton->setAutoFillBackground(true);
+  mainButton->setDefault(true);
+  mainButton->setFlat(true);
+  mainButton->setStyleSheet("text-align:center;");;
+
+  /* default font */
+  QFont font;
+  font.setPointSize(12);
+  this->setFont(font);
+
+  /* central widget */
+  centralWidget = new QWidget(this);
+  centralWidget->setObjectName(QString::fromUtf8("centralWidget"));
+  this->setCentralWidget(centralWidget);
+
+  QHBoxLayout *mainLayout = new QHBoxLayout;
+  mainLayout->addWidget(mainButton);
+  centralWidget->setLayout(mainLayout);
+
+  /* actions for te menu */
+  actionReset = new QAction(this);
+  actionReset->setObjectName(QString::fromUtf8("actionReset"));
+
+  actionResetFs = new QAction(this);
+  actionResetFs->setObjectName(QString::fromUtf8("actionResetFs"));
+
+  actionSimulated_click = new QAction(this);
+  actionSimulated_click->setObjectName(QString::fromUtf8("actionSimulated_click"));
+
+  actionPause = new QAction(this);
+  actionPause->setObjectName(QString::fromUtf8("actionPause"));
+
+  actionShow_full_list = new QAction(this);
+  actionShow_full_list->setObjectName(QString::fromUtf8("actionShow_full_list"));
+
+  actionSave_full_list = new QAction(this);
+  actionSave_full_list->setObjectName(QString::fromUtf8("actionSave_full_list"));
+
+  actionAbout = new QAction(this);
+  actionAbout->setObjectName(QString::fromUtf8("actionAbout"));
+
+  actionEnable_full_date = new QAction(this);
+  actionEnable_full_date->setObjectName(QString::fromUtf8("actionEnable_full_date"));
+  actionEnable_full_date->setCheckable(true);
+
+  actionEnable_time_and_date = new QAction(this);
+  actionEnable_time_and_date->setObjectName(QString::fromUtf8("actionEnable_time_and_date"));
+  actionEnable_time_and_date->setCheckable(true);
+
+  action_Fullscreen = new QAction(this);
+  action_Fullscreen->setObjectName(QString::fromUtf8("action_Fullscreen"));
+  action_Fullscreen->setCheckable(true);
+
+  actionChangeNumberOfLines = new QAction(this);
+  actionChangeNumberOfLines->setObjectName(QString::fromUtf8("actionChangeNumberOfLines"));
+
+  actionChangeFontSize = new QAction(this);
+  actionChangeFontSize->setObjectName(QString::fromUtf8("actionChangeFontSize"));
+
+  /* menu */
+  menuBar = new QMenuBar(this);
+  menuBar->setObjectName(QString::fromUtf8("menuBar"));
+  menuBar->setGeometry(QRect(0, 0, 800, 25));
+  menuPush_It_Menu = new QMenu(menuBar);
+  menuPush_It_Menu->setObjectName(QString::fromUtf8("menuPush_It_Menu"));
+  menuHelp = new QMenu(menuBar);
+  menuHelp->setObjectName(QString::fromUtf8("menuHelp"));
+  menuData = new QMenu(menuBar);
+  menuData->setObjectName(QString::fromUtf8("menuData"));
+  menuView = new QMenu(menuBar);
+  menuView->setObjectName(QString::fromUtf8("menuView"));
+  this->setMenuBar(menuBar);
+
+  menuBar->addAction(menuPush_It_Menu->menuAction());
+  menuBar->addAction(menuView->menuAction());
+  menuBar->addAction(menuData->menuAction());
+  menuBar->addAction(menuHelp->menuAction());
+  menuPush_It_Menu->addAction(actionReset);
+  menuPush_It_Menu->addAction(actionPause);
+  menuPush_It_Menu->addAction(actionSimulated_click);
+  menuHelp->addAction(actionAbout);
+  menuData->addAction(actionEnable_time_and_date);
+  menuData->addAction(actionEnable_full_date);
+  menuData->addSeparator();
+  menuData->addAction(actionSave_full_list);
+  menuData->addAction(actionShow_full_list);
+  menuView->addAction(action_Fullscreen);
+  menuView->addAction(actionResetFs);
+  menuView->addAction(actionChangeNumberOfLines);
+  menuView->addAction(actionChangeFontSize);
+
+  retranslateUi();
+
+  QMetaObject::connectSlotsByName(this);
+} // setupUi
+
+void MainWindow::retranslateUi()
+{
+  this->setWindowTitle(QApplication::translate("MainWindow", "Push It!", 0, QApplication::UnicodeUTF8));
+  actionReset->setText(QApplication::translate("MainWindow", "&Reset", 0, QApplication::UnicodeUTF8));
+  actionResetFs->setText(QApplication::translate("MainWindow", "&Reset+Fullscreen", 0, QApplication::UnicodeUTF8));
+  actionSimulated_click->setText(QApplication::translate("MainWindow", "Push &it!", 0, QApplication::UnicodeUTF8));
+  actionPause->setText(QApplication::translate("MainWindow", "&Pause", 0, QApplication::UnicodeUTF8));
+  actionShow_full_list->setText(QApplication::translate("MainWindow", "Show full &list", 0, QApplication::UnicodeUTF8));
+  actionSave_full_list->setText(QApplication::translate("MainWindow", "&Save full list", 0, QApplication::UnicodeUTF8));
+  actionAbout->setText(QApplication::translate("MainWindow", "&About", 0, QApplication::UnicodeUTF8));
+  actionEnable_full_date->setText(QApplication::translate("MainWindow", "Enable &full date", 0, QApplication::UnicodeUTF8));
+  actionEnable_time_and_date->setText(QApplication::translate("MainWindow", "Enable &time and date", 0, QApplication::UnicodeUTF8));
+  action_Fullscreen->setText(QApplication::translate("MainWindow", "&Fullscreen", 0, QApplication::UnicodeUTF8));
+  actionChangeNumberOfLines->setText(QApplication::translate("MainWindow", "&Lines", 0, QApplication::UnicodeUTF8));
+  actionChangeFontSize->setText(QApplication::translate("MainWindow", "Font &size", 0, QApplication::UnicodeUTF8));
+  mainButton->setText(QApplication::translate("MainWindow", "Start", 0, QApplication::UnicodeUTF8));
+  menuPush_It_Menu->setTitle(QApplication::translate("MainWindow", "&Counter", 0, QApplication::UnicodeUTF8));
+  menuHelp->setTitle(QApplication::translate("MainWindow", "&Help", 0, QApplication::UnicodeUTF8));
+  menuData->setTitle(QApplication::translate("MainWindow", "&Data", 0, QApplication::UnicodeUTF8));
+  menuView->setTitle(QApplication::translate("MainWindow", "&View", 0, QApplication::UnicodeUTF8));
+} // retranslateUi
+
+void MainWindow::changeNumberOfLines(int number)
+{
+  int maxLines = 1000;
+  bool ok = true;
+  if(number == 0)
+  {
+    number = QInputDialog::getInt(this, tr("Lines"),
+                                  tr("Number of lines to be displayed:"),
+                                  maximumNumberOfLines, 0, maxLines, 1,
+                                  &ok);
+  }
+
+  if(false == ok)
+  {
+    return;
+  }
+  else if (number > 0 && number < maxLines)
+  {
+    maximumNumberOfLines = number;
+  }
+  else
+  {
+    QString text;
+    text.sprintf("Invalid entry. Try something like \"4\" (max %i).",maxLines);
+    QMessageBox::information(this, tr("Invalid entry"),
+                             tr(text.toStdString().c_str()));
+    return;
+  }
+
+  QString actionText = actionChangeNumberOfLines->text();
+  actionText = "&Lines (";
+  actionText += QString::number(maximumNumberOfLines);
+  actionText += ")";
+  actionChangeNumberOfLines->setText(actionText);
+  mainButtonRefreshTextOnly();
+}
+
+void MainWindow::changeFontSize(int size)
+{
+  int maxSize = 100;
+  bool ok = false;
+  QFont font = mainButton->font();
+  if(size == 0)
+  {
+    size = QInputDialog::getInt(this, tr("Font size"),
+                                  tr("Font size:"),
+                                  font.pointSize(), 0, maxSize, 1,
+                                  &ok);
+  }
+  else
+  {
+    ok = true;
+  }
+
+  if(false == ok)
+  {
+    return;
+  }
+  else if (size > 0 && size < maxSize)
+  {
+    font.setPointSize(size);
+    mainButton->setFont(font);
+  }
+  else
+  {
+    QString text;
+    text.sprintf("Invalid entry. Try something like \"28\" (max %i).",maxSize);
+    QMessageBox::information(this, tr("Invalid entry"),
+                             tr(text.toStdString().c_str()));
+    return;
+  }
+
+  QString actionText = actionChangeFontSize->text();
+  actionText = "Font &size (";
+  actionText += QString::number(size);
+  actionText += ")";
+  actionChangeFontSize->setText(actionText);
+  mainButtonRefreshTextOnly();
+}
+
+/*
+function to use when the text itself is not changed but the number of lines or
+the formatting of the text
+*/
+void MainWindow::mainButtonRefreshTextOnly()
+{
+  QString string = completeString;
+  QStringList lines;
+
+  if(1 == hitCount)
+  {
+    string = getHeaderLine();
+  }
+
+  lines = string.split("\n");
+
+  /* adjust the total number of lines of the button */
+  if(lines.length() > maximumNumberOfLines)
+  {
+    while(lines.length() > maximumNumberOfLines
+          && lines.length() > 0)
+    {
+      lines.removeLast();
+    }
+  }
+  else if(lines.length() < maximumNumberOfLines)
+  {
+    while(lines.length() < maximumNumberOfLines)
+    {
+      lines.append(" ");
+    }
+  }
+
+  string = lines.join("\n");
+  this->mainButton->setText(tr(string.toStdString().c_str()));
+}
+
+void MainWindow::resetProgramAndFullscreen()
+{
+  resetProgram();
+  action_Fullscreen->trigger();
+}