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");
28 QString imgDir(topDir + "/pixmaps");
29 QString scoreFileName("score.xml");
31 QString clubFileName("club.xml");
33 QString masterFileName("club-master.xml");
35 QString logFile("/tmp/scorecard.log");
36 QString titleScores("ScoreCard - Scores");
37 QString titleCourses("ScoreCard - Courses");
39 bool dateLessThan(const Score *s1, const Score *s2)
44 bool dateMoreThan(const Score *s1, const Score *s2)
49 bool scoreMoreThan(const Score *s1, const Score *s2)
51 return s1->getTotal(Total).toInt() > s2->getTotal(Total).toInt();
54 bool scoreLessThan(const Score *s1, const Score *s2)
56 return s1->getTotal(Total).toInt() < s2->getTotal(Total).toInt();
59 // Find score based on club and course name
60 Score *MainWindow::findScore(QString & clubName, QString & courseName)
63 QListIterator<Score *> i(scoreList);
68 if ((s->getClubName() == clubName) &&
69 (s->getCourseName() == courseName))
75 // Find club based on name
76 Club *MainWindow::findClub(QString &name)
79 QListIterator<Club *> i(clubList);
84 if (c->getName() == name)
90 // Find course based on club & course name
91 Course *MainWindow::findCourse(const QString &clubName,
92 const QString &courseName)
95 QListIterator<Club *> i(clubList);
100 if (c->getName() == clubName) {
101 return c->getCourse(courseName);
107 // Find course based on current selection on the list
108 // TODO: make sure this is only called when course list is the model...
109 Course *MainWindow::currentCourse()
112 QModelIndex index = selectionModel->currentIndex();
114 if (!index.isValid())
117 const QAbstractItemModel *model = selectionModel->model();
118 QString str = model->data(index, Qt::DisplayRole).toString();
120 QStringList strList = str.split(", ");
121 if (strList.count() != 2) {
122 showNote(tr("Invalid course selection"));
125 return findCourse(strList[0], strList[1]);
128 // Find score based on current selection on the list
129 // TODO: make sure this is only called when score list is the model...
130 Score *MainWindow::currentScore()
133 QModelIndex index = selectionModel->currentIndex();
135 if (!index.isValid())
138 return scoreList.at(index.row());
141 void MainWindow::flushReadOnlyItems()
144 QMutableListIterator<Club *> i(clubList);
147 while (i.hasNext()) {
149 if (c->isReadOnly()) {
150 qDebug() << "Del:" << c->getName();
156 void MainWindow::markHomeClub()
159 QListIterator<Club *> i(clubList);
162 while (i.hasNext()) {
164 if (c->getName() == conf.homeClub)
165 c->setHomeClub(true);
167 c->setHomeClub(false);
171 void MainWindow::sortScoreList()
173 if (conf.sortOrder == "Date")
174 qSort(scoreList.begin(), scoreList.end(), dateMoreThan);
175 else if (conf.sortOrder == "Score")
176 qSort(scoreList.begin(), scoreList.end(), scoreLessThan);
179 MainWindow::MainWindow(QMainWindow *parent): QMainWindow(parent)
184 setAttribute(Qt::WA_Maemo5StackedWindow);
189 centralWidget = new QWidget(this);
191 setCentralWidget(centralWidget);
193 loadScoreFile(scoreFile, scoreList);
194 if (conf.defaultCourses == "Yes")
195 loadClubFile(masterFile, clubList, true);
196 loadClubFile(clubFile, clubList);
199 // Sort the scores based on settings
205 createListView(scoreList, clubList);
207 createLayoutList(centralWidget);
209 scoreWindow = new ScoreWindow(this);
210 courseWindow = new CourseWindow(this);
213 void MainWindow::loadSettings(void)
216 bool external = false;
222 // TODO: make via user option, automatic will never work
226 dataDir = "./" + dataDirName;
229 dataDir = mmcDir + "/" + appName + "/" + dataDirName;
232 dataDir = topDir + "/" + appName + "/" + dataDirName;
236 // Use MyDoc directory to get automatic backup/restore
237 userDataDir = QDir::homePath() + "/MyDocs/." + appName;
238 QDir dir(userDataDir);
240 if (!dir.mkpath(userDataDir)) {
241 qWarning() << "Unable to create: " + userDataDir;
245 masterFile = dataDir + "/" + masterFileName;
247 // Store user data files under $HOME so they are not lost in
249 scoreFile = userDataDir + "/" + scoreFileName;
250 clubFile = userDataDir + "/" + clubFileName;
252 // Start of 0.19 migration
253 // Copy existing user data to new location
254 // 0.18 and earlier: score.xml and club.xml are in /opt/scorecard/data
255 // 0.19 and later: score.xml and club.xml are in /home/user/MyDocs/.scorecard
256 QString scoreFileOld = dataDir + "/" + scoreFileName;
257 QString clubFileOld = dataDir + "/" + clubFileName;
259 QFile file1(scoreFileOld);
260 QFile file2(clubFileOld);
262 if (file1.exists()) {
263 move.rename(scoreFileOld, scoreFile);
264 qDebug() << "Moved: " << scoreFileOld << "->" << scoreFile;
266 if (file2.exists()) {
267 move.rename(clubFileOld, clubFile);
268 qDebug() << "Moved: " << clubFileOld << "->" << clubFile;
270 // End of 0.19 migration
272 qDebug() << "User data is at:" + userDataDir;
274 settings.beginGroup(settingsGroup);
275 conf.hcp = settings.value(settingsHcp);
276 conf.homeClub = settings.value(settingsHomeClub);
277 conf.sortOrder = settings.value(settingsSortOrder);
278 conf.userMode = settings.value(settingsUserMode);
279 conf.defaultCourses = settings.value(settingsDefaultCourses);
282 // Use default courses if no settings for that
283 if (!conf.defaultCourses.isValid())
284 conf.defaultCourses = "Yes";
286 // Use date sort order if no settings for that
287 if (!conf.sortOrder.isValid())
288 conf.sortOrder = "Date";
290 // Use basic mode if no settings for that
291 if (!conf.userMode.isValid())
292 conf.userMode = "Basic";
294 qDebug() << "Settings: " << conf.hcp << conf.homeClub << conf.sortOrder << conf.userMode << conf.defaultCourses;
297 void MainWindow::saveSettings(void)
300 settings.beginGroup(settingsGroup);
301 if (conf.hcp.isValid())
302 settings.setValue(settingsHcp, conf.hcp);
303 if (conf.homeClub.isValid())
304 settings.setValue(settingsHomeClub, conf.homeClub);
305 if (conf.sortOrder.isValid())
306 settings.setValue(settingsSortOrder, conf.sortOrder);
307 if (conf.userMode.isValid())
308 settings.setValue(settingsUserMode, conf.userMode);
309 if (conf.defaultCourses.isValid())
310 settings.setValue(settingsDefaultCourses, conf.defaultCourses);
314 void MainWindow::createLayoutList(QWidget *parent)
317 QVBoxLayout * tableLayout = new QVBoxLayout;
318 tableLayout->addWidget(list);
320 QHBoxLayout *mainLayout = new QHBoxLayout(parent);
321 mainLayout->addLayout(tableLayout);
322 parent->setLayout(mainLayout);
325 void MainWindow::createListView(QList<Score *> &scoreList,
326 QList <Club *> &clubList)
329 list = new QListView(this);
331 scoreListModel = new ScoreListModel(scoreList, clubList);
332 courseListModel = new CourseListModel(clubList);
334 list->setStyleSheet(defaultStyleSheet);
336 list->setSelectionMode(QAbstractItemView::SingleSelection);
337 list->setProperty("FingerScrolling", true);
342 connect(list, SIGNAL(clicked(QModelIndex)),
343 this, SLOT(clickedList(QModelIndex)));
346 void MainWindow::listScores()
349 list->setModel(scoreListModel);
350 selectionModel = list->selectionModel();
351 updateTitleBar(titleScores);
354 void MainWindow::listCourses()
357 list->setModel(courseListModel);
358 selectionModel = list->selectionModel();
359 updateTitleBar(titleCourses);
362 void MainWindow::createActions()
365 newScoreAction = new QAction(tr("New Score"), this);
366 connect(newScoreAction, SIGNAL(triggered()), this, SLOT(newScore()));
368 newCourseAction = new QAction(tr("New Course"), this);
369 connect(newCourseAction, SIGNAL(triggered()), this, SLOT(newCourse()));
371 statAction = new QAction(tr("Statistics"), this);
372 connect(statAction, SIGNAL(triggered()), this, SLOT(viewStatistics()));
374 settingsAction = new QAction(tr("Settings"), this);
375 connect(settingsAction, SIGNAL(triggered()), this, SLOT(viewSettings()));
377 // Maemo5 style menu filters
378 filterGroup = new QActionGroup(this);
379 filterGroup->setExclusive(true);
381 listScoreAction = new QAction(tr("Scores"), filterGroup);
382 listScoreAction->setCheckable(true);
383 listScoreAction->setChecked(true);
384 connect(listScoreAction, SIGNAL(triggered()), this, SLOT(listScores()));
386 listCourseAction = new QAction(tr("Courses"), filterGroup);
387 listCourseAction->setCheckable(true);
388 connect(listCourseAction, SIGNAL(triggered()), this, SLOT(listCourses()));
391 void MainWindow::createMenus()
395 menu = menuBar()->addMenu("");
397 menu = menuBar()->addMenu("Menu");
400 menu->addAction(newScoreAction);
401 menu->addAction(newCourseAction);
402 menu->addAction(statAction);
403 menu->addAction(settingsAction);
404 menu->addActions(filterGroup->actions());
407 void MainWindow::updateTitleBar(QString & msg)
413 void MainWindow::showNote(QString msg)
416 QMaemo5InformationBox::information(this,
418 QMaemo5InformationBox::DefaultTimeout);
422 void MainWindow::clickedList(const QModelIndex &index)
425 int row = index.row();
427 const QAbstractItemModel *m = index.model();
428 if (m == scoreListModel) {
429 if (row < scoreList.count()) {
430 Score * score = scoreList.at(row);
431 Course * course = findCourse(score->getClubName(), score->getCourseName());
432 viewScore(score, course);
435 else if (m == courseListModel) {
436 QString str = courseListModel->data(index, Qt::DisplayRole).toString();
437 QStringList strList = str.split(", ");
439 if (strList.count() != 2) {
440 showNote(QString("Invalid course selection"));
443 Course * course = findCourse(strList.at(0), strList.at(1));
448 void MainWindow::viewScore(Score * score, Course * course)
451 scoreWindow->setup(score, course);
455 void MainWindow::newScore()
458 SelectDialog *selectDialog = new SelectDialog(this);
460 selectDialog->init(clubList);
462 int result = selectDialog->exec();
468 selectDialog->results(clubName, courseName, date);
470 ScoreDialog *scoreDialog;
471 if (conf.userMode == "Basic")
472 scoreDialog = (ScoreDialog *)new ScoreDialog18(this);
474 scoreDialog = (ScoreDialog *)new ScoreDialogSingle(this);
476 QString title = "New Score: " + courseName + ", " + date;
477 scoreDialog->setWindowTitle(title);
479 qDebug() << clubName << courseName;
481 Club *club = findClub(clubName);
483 showNote(tr("No club"));
486 Course *course = club->getCourse(courseName);
488 showNote(tr("Error: no such course:"));
492 scoreDialog->init(course);
493 result = scoreDialog->exec();
495 QVector<QString> scores(18);
497 scoreDialog->results(scores);
498 Score *score = new Score(scores, clubName, courseName, date);
501 // Sort the scores based on settings
504 saveScoreFile(scoreFile, scoreList);
505 scoreListModel->update(scoreList);
511 void MainWindow::editScore()
515 Score *score = currentScore();
518 course = findCourse(score->getClubName(), score->getCourseName());
520 if (!course || !score) {
521 showNote(tr("No score or course to edit"));
525 QString date = score->getDate();
527 ScoreDialog18 *scoreDialog = new ScoreDialog18(this);
528 scoreDialog->init(course, score);
530 QString title = "Edit Score: " + course->getName() + ", " + date;
531 scoreDialog->setWindowTitle(title);
533 int result = scoreDialog->exec();
535 QVector<QString> scores(18);
537 scoreDialog->results(scores);
539 score->update(scores);
541 // Sort the scores based on dates
542 qSort(scoreList.begin(), scoreList.end(), dateMoreThan);
544 saveScoreFile(scoreFile, scoreList);
550 void MainWindow::deleteScore()
554 scoreWindow->close();
556 QModelIndex index = selectionModel->currentIndex();
557 if (!index.isValid()) {
558 qDebug() << "Invalid index";
562 scoreList.removeAt(index.row());
564 saveScoreFile(scoreFile, scoreList);
565 scoreListModel->update(scoreList);
569 void MainWindow::viewCourse(Course * course)
572 courseWindow->setup(course);
573 courseWindow->show();
576 void MainWindow::newCourse()
579 CourseSelectDialog *selectDialog = new CourseSelectDialog(this);
581 int result = selectDialog->exec();
587 selectDialog->results(clubName, courseName);
589 CourseDialog *courseDialog = new CourseDialog(this);
590 courseDialog->init();
591 QString title = "New Course: " + clubName + "," + courseName;
592 courseDialog->setWindowTitle(title);
594 int result = courseDialog->exec();
596 QVector<QString> par(18);
597 QVector<QString> hcp(18);
598 QVector<QString> len(18);
600 courseDialog->results(par, hcp, len);
603 Club *club = findClub(clubName);
605 course = club->getCourse(courseName);
607 showNote(tr("Club/course already in the database"));
611 course = new Course(courseName, par, hcp);
612 club->addCourse(course);
616 // New club and course
617 club = new Club(clubName);
618 course = new Course(courseName, par, hcp);
619 club->addCourse(course);
623 saveClubFile(clubFile, clubList);
624 courseListModel->update(clubList);
630 void MainWindow::editCourse()
633 Course *course = currentCourse();
636 showNote(tr("No course on edit"));
640 CourseDialog *courseDialog = new CourseDialog(this);
641 courseDialog->init(course);
643 QString title = "Edit Course: " + course->getName();
644 courseDialog->setWindowTitle(title);
646 int result = courseDialog->exec();
648 QVector<QString> par(18);
649 QVector<QString> hcp(18);
650 QVector<QString> len(18);
652 courseDialog->results(par, hcp, len);
654 course->update(par, hcp, len);
655 saveClubFile(clubFile, clubList);
661 void MainWindow::deleteCourse()
665 Course * course = currentCourse();
668 qDebug() << "Invalid course for deletion";
671 club = course->parent();
673 // Can not delete course if it has scores -- check
674 if (findScore(club->getName(), course->getName()) != 0) {
675 showNote(tr("Can not delete course, delete scores on the course first"));
680 courseWindow->close();
682 club->delCourse(course);
684 if (club->isEmpty()) {
685 int index = clubList.indexOf(club);
687 clubList.removeAt(index);
691 saveClubFile(clubFile, clubList);
692 courseListModel->update(clubList);
696 void MainWindow::viewStatistics()
699 QMainWindow *win = new QMainWindow(this);
700 QString title = "Statistics";
701 win->setWindowTitle(title);
703 win->setAttribute(Qt::WA_Maemo5StackedWindow);
706 StatModel *model = new StatModel(clubList, scoreList);
708 QTableView *table = new QTableView;
710 table->setSelectionMode(QAbstractItemView::NoSelection);
711 table->setStyleSheet(statStyleSheet);
712 table->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
713 table->verticalHeader()->setResizeMode(QHeaderView::Stretch);
714 table->verticalHeader()->setAutoFillBackground(true);
715 table->setModel(model);
717 QWidget *central = new QWidget(win);
718 win->setCentralWidget(central);
720 QTextEdit *textEdit = new QTextEdit;
722 textEdit->setReadOnly(true);
724 QVBoxLayout *infoLayout = new QVBoxLayout;
725 infoLayout->addWidget(table);
727 QHBoxLayout *mainLayout = new QHBoxLayout(central);
728 mainLayout->addLayout(infoLayout);
729 central->setLayout(mainLayout);
734 void MainWindow::viewSettings()
737 SettingsDialog *dlg = new SettingsDialog(this);
739 dlg->init(conf, clubList);
741 int result = dlg->exec();
743 QString oldValue = conf.defaultCourses.toString();
745 QString newValue = conf.defaultCourses.toString();
748 // Reload club list, or drop r/o courses from list
749 if (oldValue == "Yes" && newValue == "No") {
750 flushReadOnlyItems();
751 courseListModel->update(clubList);
754 else if ((oldValue == "No" || oldValue == "") && newValue == "Yes") {
755 loadClubFile(masterFile, clubList, true);
756 courseListModel->update(clubList);
759 // TODO: do these only if needed
762 scoreListModel->update(scoreList);
767 void MainWindow::loadScoreFile(QString &fileName, QList<Score *> &list)
769 ScoreXmlHandler handler(list);
771 if (handler.parse(fileName))
772 qDebug() << "File loaded:" << fileName << " entries:" << list.size();
775 void MainWindow::saveScoreFile(QString &fileName, QList<Score *> &list)
777 ScoreXmlHandler handler(list);
779 if (handler.save(fileName))
781 qDebug() << "File saved:" << fileName << " entries:" << list.size();
783 qWarning() << "Unable to save:" << fileName;
786 void MainWindow::loadClubFile(QString &fileName, QList<Club *> &list, bool readOnly)
788 ClubXmlHandler handler(list);
790 if (handler.parse(fileName, readOnly))
791 qDebug() << "File loaded:" << fileName << " entries:" << list.size();
794 void MainWindow::saveClubFile(QString &fileName, QList<Club *> &list)
796 ClubXmlHandler handler(list);
798 if (handler.save(fileName))
800 qDebug() << "File saved:" << fileName << " entries:" << list.size();
802 qWarning() << "Unable to save:" << fileName;