2 * Copyright (C) 2009 Sakari Poussa
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, version 2.
11 #include <QMaemo5InformationBox>
14 #include "score-common.h"
15 #include "main-window.h"
16 #include "score-dialog.h"
17 #include "course-dialog.h"
18 #include "settings-dialog.h"
19 #include "stat-model.h"
20 #include "xml-dom-parser.h"
22 QString appName("scorecard");
23 QString topDir("/opt");
24 QString mmcDir("/media/mmc1");
25 QString dataDirName("data");
27 QString imgDir(topDir + "/pixmaps");
28 QString scoreFileName("score.xml");
30 QString clubFileName("club.xml");
32 QString masterFileName("club-master.xml");
34 QString logFile("/tmp/scorecard.log");
35 QString titleScores("ScoreCard - Scores");
36 QString titleCourses("ScoreCard - Courses");
38 bool dateLessThan(const Score *s1, const Score *s2)
43 bool dateMoreThan(const Score *s1, const Score *s2)
48 bool scoreMoreThan(const Score *s1, const Score *s2)
50 return s1->getTotal(Total).toInt() > s2->getTotal(Total).toInt();
53 bool scoreLessThan(const Score *s1, const Score *s2)
55 return s1->getTotal(Total).toInt() < s2->getTotal(Total).toInt();
58 // Find score based on club and course name
59 Score *MainWindow::findScore(QString & clubName, QString & courseName)
62 QListIterator<Score *> i(scoreList);
67 if ((s->getClubName() == clubName) &&
68 (s->getCourseName() == courseName))
74 // Find club based on name
75 Club *MainWindow::findClub(QString &name)
78 QListIterator<Club *> i(clubList);
83 if (c->getName() == name)
89 // Find course based on club & course name
90 Course *MainWindow::findCourse(const QString &clubName,
91 const QString &courseName)
94 QListIterator<Club *> i(clubList);
99 if (c->getName() == clubName) {
100 return c->getCourse(courseName);
106 // Find course based on current selection on the list
107 // TODO: make sure this is only called when course list is the model...
108 Course *MainWindow::currentCourse()
111 QModelIndex index = selectionModel->currentIndex();
113 if (!index.isValid())
116 const QAbstractItemModel *model = selectionModel->model();
117 QString str = model->data(index, Qt::DisplayRole).toString();
119 QStringList strList = str.split(", ");
120 if (strList.count() != 2) {
121 showNote(tr("Invalid course selection"));
124 return findCourse(strList[0], strList[1]);
127 // Find score based on current selection on the list
128 // TODO: make sure this is only called when score list is the model...
129 Score *MainWindow::currentScore()
132 QModelIndex index = selectionModel->currentIndex();
134 if (!index.isValid())
137 return scoreList.at(index.row());
140 void MainWindow::flushReadOnlyItems()
143 QMutableListIterator<Club *> i(clubList);
146 while (i.hasNext()) {
148 if (c->isReadOnly()) {
149 qDebug() << "Del:" << c->getName();
155 void MainWindow::markHomeClub()
158 QListIterator<Club *> i(clubList);
161 while (i.hasNext()) {
163 if (c->getName() == conf.homeClub)
164 c->setHomeClub(true);
166 c->setHomeClub(false);
170 void MainWindow::sortScoreList()
172 if (conf.sortOrder == "Date")
173 qSort(scoreList.begin(), scoreList.end(), dateMoreThan);
174 else if (conf.sortOrder == "Score")
175 qSort(scoreList.begin(), scoreList.end(), scoreLessThan);
178 MainWindow::MainWindow(QMainWindow *parent): QMainWindow(parent)
183 setAttribute(Qt::WA_Maemo5StackedWindow);
188 centralWidget = new QWidget(this);
190 setCentralWidget(centralWidget);
192 loadScoreFile(scoreFile, scoreList);
193 if (conf.defaultCourses == "Yes")
194 loadClubFile(masterFile, clubList, true);
195 loadClubFile(clubFile, clubList);
198 // Sort the scores based on settings
204 createListView(scoreList, clubList);
206 createLayoutList(centralWidget);
208 scoreWindow = new ScoreWindow(this);
209 courseWindow = new CourseWindow(this);
212 void MainWindow::loadSettings(void)
215 bool external = false;
221 // TODO: make via user option, automatic will never work
225 dataDir = "./" + dataDirName;
228 dataDir = mmcDir + "/" + appName + "/" + dataDirName;
231 dataDir = topDir + "/" + appName + "/" + dataDirName;
234 scoreFile = dataDir + "/" + scoreFileName;
235 clubFile = dataDir + "/" + clubFileName;
236 masterFile = dataDir + "/" + masterFileName;
240 if (!dir.mkpath(dataDir)) {
241 qWarning() << "Unable to create: " + dataDir;
244 qDebug() << "Data is at:" + dataDir;
246 settings.beginGroup(settingsGroup);
247 conf.hcp = settings.value(settingsHcp);
248 conf.homeClub = settings.value(settingsHomeClub);
249 conf.sortOrder = settings.value(settingsSortOrder);
250 conf.defaultCourses = settings.value(settingsDefaultCourses);
253 // Use default courses if no settings for that
254 if (!conf.defaultCourses.isValid())
255 conf.defaultCourses = "Yes";
257 // Use date sort order if no settings for that
258 if (!conf.sortOrder.isValid())
259 conf.sortOrder = "Date";
261 qDebug() << "Settings: " << conf.hcp << conf.homeClub << conf.sortOrder << conf.defaultCourses;
264 void MainWindow::saveSettings(void)
267 settings.beginGroup(settingsGroup);
268 if (conf.hcp.isValid())
269 settings.setValue(settingsHcp, conf.hcp);
270 if (conf.homeClub.isValid())
271 settings.setValue(settingsHomeClub, conf.homeClub);
272 if (conf.sortOrder.isValid())
273 settings.setValue(settingsSortOrder, conf.sortOrder);
274 if (conf.defaultCourses.isValid())
275 settings.setValue(settingsDefaultCourses, conf.defaultCourses);
279 void MainWindow::createLayoutList(QWidget *parent)
282 QVBoxLayout * tableLayout = new QVBoxLayout;
283 tableLayout->addWidget(list);
285 QHBoxLayout *mainLayout = new QHBoxLayout(parent);
286 mainLayout->addLayout(tableLayout);
287 parent->setLayout(mainLayout);
290 void MainWindow::createListView(QList<Score *> &scoreList,
291 QList <Club *> &clubList)
294 list = new QListView(this);
296 scoreListModel = new ScoreListModel(scoreList, clubList);
297 courseListModel = new CourseListModel(clubList);
299 list->setStyleSheet(defaultStyleSheet);
301 list->setSelectionMode(QAbstractItemView::SingleSelection);
302 list->setProperty("FingerScrolling", true);
307 connect(list, SIGNAL(clicked(QModelIndex)),
308 this, SLOT(clickedList(QModelIndex)));
311 void MainWindow::listScores()
314 list->setModel(scoreListModel);
315 selectionModel = list->selectionModel();
316 updateTitleBar(titleScores);
319 void MainWindow::listCourses()
322 list->setModel(courseListModel);
323 selectionModel = list->selectionModel();
324 updateTitleBar(titleCourses);
327 void MainWindow::createActions()
330 newScoreAction = new QAction(tr("New Score"), this);
331 connect(newScoreAction, SIGNAL(triggered()), this, SLOT(newScore()));
333 newCourseAction = new QAction(tr("New Course"), this);
334 connect(newCourseAction, SIGNAL(triggered()), this, SLOT(newCourse()));
336 statAction = new QAction(tr("Statistics"), this);
337 connect(statAction, SIGNAL(triggered()), this, SLOT(viewStatistics()));
339 settingsAction = new QAction(tr("Settings"), this);
340 connect(settingsAction, SIGNAL(triggered()), this, SLOT(viewSettings()));
342 // Maemo5 style menu filters
343 filterGroup = new QActionGroup(this);
344 filterGroup->setExclusive(true);
346 listScoreAction = new QAction(tr("Scores"), filterGroup);
347 listScoreAction->setCheckable(true);
348 listScoreAction->setChecked(true);
349 connect(listScoreAction, SIGNAL(triggered()), this, SLOT(listScores()));
351 listCourseAction = new QAction(tr("Courses"), filterGroup);
352 listCourseAction->setCheckable(true);
353 connect(listCourseAction, SIGNAL(triggered()), this, SLOT(listCourses()));
356 void MainWindow::createMenus()
360 menu = menuBar()->addMenu("");
362 menu = menuBar()->addMenu("Menu");
365 menu->addAction(newScoreAction);
366 menu->addAction(newCourseAction);
367 menu->addAction(statAction);
368 menu->addAction(settingsAction);
369 menu->addActions(filterGroup->actions());
372 void MainWindow::updateTitleBar(QString & msg)
378 void MainWindow::showNote(QString msg)
381 QMaemo5InformationBox::information(this,
383 QMaemo5InformationBox::DefaultTimeout);
387 void MainWindow::clickedList(const QModelIndex &index)
390 int row = index.row();
392 const QAbstractItemModel *m = index.model();
393 if (m == scoreListModel) {
394 if (row < scoreList.count()) {
395 Score * score = scoreList.at(row);
396 Course * course = findCourse(score->getClubName(), score->getCourseName());
397 viewScore(score, course);
400 else if (m == courseListModel) {
401 QString str = courseListModel->data(index, Qt::DisplayRole).toString();
402 QStringList strList = str.split(", ");
404 if (strList.count() != 2) {
405 showNote(QString("Invalid course selection"));
408 Course * course = findCourse(strList.at(0), strList.at(1));
413 void MainWindow::viewScore(Score * score, Course * course)
416 scoreWindow->setup(score, course);
420 void MainWindow::newScore()
423 SelectDialog *selectDialog = new SelectDialog(this);
425 selectDialog->init(clubList);
427 int result = selectDialog->exec();
433 selectDialog->results(clubName, courseName, date);
435 ScoreDialog *scoreDialog = new ScoreDialog(this);
436 QString title = "New Score: " + courseName + ", " + date;
437 scoreDialog->setWindowTitle(title);
439 Club *club = findClub(clubName);
441 showNote(tr("Error: no such club"));
444 Course *course = club->getCourse(courseName);
446 showNote(tr("Error: no such course:"));
449 scoreDialog->init(course);
450 result = scoreDialog->exec();
452 QVector<QString> scores(18);
454 scoreDialog->results(scores);
455 Score *score = new Score(scores, clubName, courseName, date);
458 // Sort the scores based on settings
461 saveScoreFile(scoreFile, scoreList);
462 scoreListModel->update(scoreList);
468 void MainWindow::editScore()
472 Score *score = currentScore();
475 course = findCourse(score->getClubName(), score->getCourseName());
477 if (!course || !score) {
478 qDebug() << "No score/course to edit";
482 QString date = score->getDate();
484 ScoreDialog *scoreDialog = new ScoreDialog(this);
485 scoreDialog->init(course, score);
487 QString title = "Edit Score: " + course->getName() + ", " + date;
488 scoreDialog->setWindowTitle(title);
490 int result = scoreDialog->exec();
492 QVector<QString> scores(18);
494 scoreDialog->results(scores);
496 score->update(scores);
498 // Sort the scores based on dates
499 qSort(scoreList.begin(), scoreList.end(), dateMoreThan);
501 saveScoreFile(scoreFile, scoreList);
507 void MainWindow::deleteScore()
511 scoreWindow->close();
513 QModelIndex index = selectionModel->currentIndex();
514 if (!index.isValid()) {
515 qDebug() << "Invalid index";
519 scoreList.removeAt(index.row());
521 saveScoreFile(scoreFile, scoreList);
522 scoreListModel->update(scoreList);
526 void MainWindow::viewCourse(Course * course)
529 courseWindow->setup(course);
530 courseWindow->show();
533 void MainWindow::newCourse()
536 CourseSelectDialog *selectDialog = new CourseSelectDialog(this);
538 int result = selectDialog->exec();
544 selectDialog->results(clubName, courseName);
546 CourseDialog *courseDialog = new CourseDialog(this);
547 courseDialog->init();
548 QString title = "New Course: " + clubName + "," + courseName;
549 courseDialog->setWindowTitle(title);
551 int result = courseDialog->exec();
553 QVector<QString> par(18);
554 QVector<QString> hcp(18);
555 QVector<QString> len(18);
557 courseDialog->results(par, hcp, len);
560 Club *club = findClub(clubName);
562 course = club->getCourse(courseName);
564 showNote(tr("Club/course already in the database"));
568 course = new Course(courseName, par, hcp);
569 club->addCourse(course);
573 // New club and course
574 club = new Club(clubName);
575 course = new Course(courseName, par, hcp);
576 club->addCourse(course);
580 saveClubFile(clubFile, clubList);
581 courseListModel->update(clubList);
587 void MainWindow::editCourse()
590 Course *course = currentCourse();
593 showNote(tr("No course on edit"));
597 CourseDialog *courseDialog = new CourseDialog(this);
598 courseDialog->init(course);
600 QString title = "Edit Course: " + course->getName();
601 courseDialog->setWindowTitle(title);
603 int result = courseDialog->exec();
605 QVector<QString> par(18);
606 QVector<QString> hcp(18);
607 QVector<QString> len(18);
609 courseDialog->results(par, hcp, len);
611 course->update(par, hcp, len);
612 saveClubFile(clubFile, clubList);
618 void MainWindow::deleteCourse()
622 Course * course = currentCourse();
625 qDebug() << "Invalid course for deletion";
628 club = course->parent();
630 // Can not delete course if it has scores -- check
631 if (findScore(club->getName(), course->getName()) != 0) {
632 showNote(tr("Can not delete course, delete scores on the course first"));
637 courseWindow->close();
639 club->delCourse(course);
641 if (club->isEmpty()) {
642 int index = clubList.indexOf(club);
644 clubList.removeAt(index);
648 saveClubFile(clubFile, clubList);
649 courseListModel->update(clubList);
653 void MainWindow::viewStatistics()
656 QMainWindow *win = new QMainWindow(this);
657 QString title = "Statistics";
658 win->setWindowTitle(title);
660 win->setAttribute(Qt::WA_Maemo5StackedWindow);
663 StatModel *model = new StatModel(clubList, scoreList);
665 QTableView *table = new QTableView;
667 table->setSelectionMode(QAbstractItemView::NoSelection);
668 table->setStyleSheet(statStyleSheet);
669 table->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
670 table->verticalHeader()->setResizeMode(QHeaderView::Stretch);
671 table->verticalHeader()->setAutoFillBackground(true);
672 table->setModel(model);
674 QWidget *central = new QWidget(win);
675 win->setCentralWidget(central);
677 QTextEdit *textEdit = new QTextEdit;
679 textEdit->setReadOnly(true);
681 QVBoxLayout *infoLayout = new QVBoxLayout;
682 infoLayout->addWidget(table);
684 QHBoxLayout *mainLayout = new QHBoxLayout(central);
685 mainLayout->addLayout(infoLayout);
686 central->setLayout(mainLayout);
691 void MainWindow::viewSettings()
694 SettingsDialog *dlg = new SettingsDialog(this);
696 dlg->init(conf, clubList);
698 int result = dlg->exec();
700 QString oldValue = conf.defaultCourses.toString();
702 QString newValue = conf.defaultCourses.toString();
705 // Reload club list, or drop r/o courses from list
706 if (oldValue == "Yes" && newValue == "No") {
707 flushReadOnlyItems();
708 courseListModel->update(clubList);
711 else if ((oldValue == "No" || oldValue == "") && newValue == "Yes") {
712 loadClubFile(masterFile, clubList, true);
713 courseListModel->update(clubList);
716 // TODO: do these only if needed
719 scoreListModel->update(scoreList);
724 void MainWindow::loadScoreFile(QString &fileName, QList<Score *> &list)
726 ScoreXmlHandler handler(list);
728 if (handler.parse(fileName))
729 qDebug() << "File loaded:" << fileName << " entries:" << list.size();
732 void MainWindow::saveScoreFile(QString &fileName, QList<Score *> &list)
734 ScoreXmlHandler handler(list);
736 if (handler.save(fileName))
738 qDebug() << "File saved:" << fileName << " entries:" << list.size();
740 qWarning() << "Unable to save:" << fileName;
743 void MainWindow::loadClubFile(QString &fileName, QList<Club *> &list, bool readOnly)
745 ClubXmlHandler handler(list);
747 if (handler.parse(fileName, readOnly))
748 qDebug() << "File loaded:" << fileName << " entries:" << list.size();
751 void MainWindow::saveClubFile(QString &fileName, QList<Club *> &list)
753 ClubXmlHandler handler(list);
755 if (handler.save(fileName))
757 qDebug() << "File saved:" << fileName << " entries:" << list.size();
759 qWarning() << "Unable to save:" << fileName;