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 userDataDir = QDir::homePath() + "/." + appName;
237 QDir dir(userDataDir);
239 if (!dir.mkpath(userDataDir)) {
240 qWarning() << "Unable to create: " + userDataDir;
244 masterFile = dataDir + "/" + masterFileName;
246 // Store user data files under $HOME so they are not lost in
248 scoreFile = userDataDir + "/" + scoreFileName;
249 clubFile = userDataDir + "/" + clubFileName;
251 // Start of 0.19 migration
252 // Copy existing user data to new location
253 // 0.18 and earlier: score.xml and club.xml are in /opt/scorecard/data
254 // 0.19 and later: score.xml and club.xml are in /home/user/.scorecard
255 QString scoreFileOld = dataDir + "/" + scoreFileName;
256 QString clubFileOld = dataDir + "/" + clubFileName;
258 QFile file1(scoreFileOld);
259 QFile file2(clubFileOld);
261 if (file1.exists()) {
262 move.rename(scoreFileOld, scoreFile);
263 qDebug() << "Moved: " << scoreFileOld << "->" << scoreFile;
265 if (file2.exists()) {
266 move.rename(clubFileOld, clubFile);
267 qDebug() << "Moved: " << clubFileOld << "->" << clubFile;
269 // End of 0.19 migration
271 qDebug() << "User data is at:" + userDataDir;
273 settings.beginGroup(settingsGroup);
274 conf.hcp = settings.value(settingsHcp);
275 conf.homeClub = settings.value(settingsHomeClub);
276 conf.sortOrder = settings.value(settingsSortOrder);
277 conf.defaultCourses = settings.value(settingsDefaultCourses);
280 // Use default courses if no settings for that
281 if (!conf.defaultCourses.isValid())
282 conf.defaultCourses = "Yes";
284 // Use date sort order if no settings for that
285 if (!conf.sortOrder.isValid())
286 conf.sortOrder = "Date";
288 qDebug() << "Settings: " << conf.hcp << conf.homeClub << conf.sortOrder << conf.defaultCourses;
291 void MainWindow::saveSettings(void)
294 settings.beginGroup(settingsGroup);
295 if (conf.hcp.isValid())
296 settings.setValue(settingsHcp, conf.hcp);
297 if (conf.homeClub.isValid())
298 settings.setValue(settingsHomeClub, conf.homeClub);
299 if (conf.sortOrder.isValid())
300 settings.setValue(settingsSortOrder, conf.sortOrder);
301 if (conf.defaultCourses.isValid())
302 settings.setValue(settingsDefaultCourses, conf.defaultCourses);
306 void MainWindow::createLayoutList(QWidget *parent)
309 QVBoxLayout * tableLayout = new QVBoxLayout;
310 tableLayout->addWidget(list);
312 QHBoxLayout *mainLayout = new QHBoxLayout(parent);
313 mainLayout->addLayout(tableLayout);
314 parent->setLayout(mainLayout);
317 void MainWindow::createListView(QList<Score *> &scoreList,
318 QList <Club *> &clubList)
321 list = new QListView(this);
323 scoreListModel = new ScoreListModel(scoreList, clubList);
324 courseListModel = new CourseListModel(clubList);
326 list->setStyleSheet(defaultStyleSheet);
328 list->setSelectionMode(QAbstractItemView::SingleSelection);
329 list->setProperty("FingerScrolling", true);
334 connect(list, SIGNAL(clicked(QModelIndex)),
335 this, SLOT(clickedList(QModelIndex)));
338 void MainWindow::listScores()
341 list->setModel(scoreListModel);
342 selectionModel = list->selectionModel();
343 updateTitleBar(titleScores);
346 void MainWindow::listCourses()
349 list->setModel(courseListModel);
350 selectionModel = list->selectionModel();
351 updateTitleBar(titleCourses);
354 void MainWindow::createActions()
357 newScoreAction = new QAction(tr("New Score"), this);
358 connect(newScoreAction, SIGNAL(triggered()), this, SLOT(newScore()));
360 newCourseAction = new QAction(tr("New Course"), this);
361 connect(newCourseAction, SIGNAL(triggered()), this, SLOT(newCourse()));
363 statAction = new QAction(tr("Statistics"), this);
364 connect(statAction, SIGNAL(triggered()), this, SLOT(viewStatistics()));
366 settingsAction = new QAction(tr("Settings"), this);
367 connect(settingsAction, SIGNAL(triggered()), this, SLOT(viewSettings()));
369 // Maemo5 style menu filters
370 filterGroup = new QActionGroup(this);
371 filterGroup->setExclusive(true);
373 listScoreAction = new QAction(tr("Scores"), filterGroup);
374 listScoreAction->setCheckable(true);
375 listScoreAction->setChecked(true);
376 connect(listScoreAction, SIGNAL(triggered()), this, SLOT(listScores()));
378 listCourseAction = new QAction(tr("Courses"), filterGroup);
379 listCourseAction->setCheckable(true);
380 connect(listCourseAction, SIGNAL(triggered()), this, SLOT(listCourses()));
383 void MainWindow::createMenus()
387 menu = menuBar()->addMenu("");
389 menu = menuBar()->addMenu("Menu");
392 menu->addAction(newScoreAction);
393 menu->addAction(newCourseAction);
394 menu->addAction(statAction);
395 menu->addAction(settingsAction);
396 menu->addActions(filterGroup->actions());
399 void MainWindow::updateTitleBar(QString & msg)
405 void MainWindow::showNote(QString msg)
408 QMaemo5InformationBox::information(this,
410 QMaemo5InformationBox::DefaultTimeout);
414 void MainWindow::clickedList(const QModelIndex &index)
417 int row = index.row();
419 const QAbstractItemModel *m = index.model();
420 if (m == scoreListModel) {
421 if (row < scoreList.count()) {
422 Score * score = scoreList.at(row);
423 Course * course = findCourse(score->getClubName(), score->getCourseName());
424 viewScore(score, course);
427 else if (m == courseListModel) {
428 QString str = courseListModel->data(index, Qt::DisplayRole).toString();
429 QStringList strList = str.split(", ");
431 if (strList.count() != 2) {
432 showNote(QString("Invalid course selection"));
435 Course * course = findCourse(strList.at(0), strList.at(1));
440 void MainWindow::viewScore(Score * score, Course * course)
443 scoreWindow->setup(score, course);
447 void MainWindow::newScore()
450 SelectDialog *selectDialog = new SelectDialog(this);
452 selectDialog->init(clubList);
454 int result = selectDialog->exec();
460 selectDialog->results(clubName, courseName, date);
462 ScoreDialog *scoreDialog = new ScoreDialog(this);
463 QString title = "New Score: " + courseName + ", " + date;
464 scoreDialog->setWindowTitle(title);
466 Club *club = findClub(clubName);
468 showNote(tr("Error: no such club"));
471 Course *course = club->getCourse(courseName);
473 showNote(tr("Error: no such course:"));
476 scoreDialog->init(course);
477 result = scoreDialog->exec();
479 QVector<QString> scores(18);
481 scoreDialog->results(scores);
482 Score *score = new Score(scores, clubName, courseName, date);
485 // Sort the scores based on settings
488 saveScoreFile(scoreFile, scoreList);
489 scoreListModel->update(scoreList);
495 void MainWindow::editScore()
499 Score *score = currentScore();
502 course = findCourse(score->getClubName(), score->getCourseName());
504 if (!course || !score) {
505 qDebug() << "No score/course to edit";
509 QString date = score->getDate();
511 ScoreDialog *scoreDialog = new ScoreDialog(this);
512 scoreDialog->init(course, score);
514 QString title = "Edit Score: " + course->getName() + ", " + date;
515 scoreDialog->setWindowTitle(title);
517 int result = scoreDialog->exec();
519 QVector<QString> scores(18);
521 scoreDialog->results(scores);
523 score->update(scores);
525 // Sort the scores based on dates
526 qSort(scoreList.begin(), scoreList.end(), dateMoreThan);
528 saveScoreFile(scoreFile, scoreList);
534 void MainWindow::deleteScore()
538 scoreWindow->close();
540 QModelIndex index = selectionModel->currentIndex();
541 if (!index.isValid()) {
542 qDebug() << "Invalid index";
546 scoreList.removeAt(index.row());
548 saveScoreFile(scoreFile, scoreList);
549 scoreListModel->update(scoreList);
553 void MainWindow::viewCourse(Course * course)
556 courseWindow->setup(course);
557 courseWindow->show();
560 void MainWindow::newCourse()
563 CourseSelectDialog *selectDialog = new CourseSelectDialog(this);
565 int result = selectDialog->exec();
571 selectDialog->results(clubName, courseName);
573 CourseDialog *courseDialog = new CourseDialog(this);
574 courseDialog->init();
575 QString title = "New Course: " + clubName + "," + courseName;
576 courseDialog->setWindowTitle(title);
578 int result = courseDialog->exec();
580 QVector<QString> par(18);
581 QVector<QString> hcp(18);
582 QVector<QString> len(18);
584 courseDialog->results(par, hcp, len);
587 Club *club = findClub(clubName);
589 course = club->getCourse(courseName);
591 showNote(tr("Club/course already in the database"));
595 course = new Course(courseName, par, hcp);
596 club->addCourse(course);
600 // New club and course
601 club = new Club(clubName);
602 course = new Course(courseName, par, hcp);
603 club->addCourse(course);
607 saveClubFile(clubFile, clubList);
608 courseListModel->update(clubList);
614 void MainWindow::editCourse()
617 Course *course = currentCourse();
620 showNote(tr("No course on edit"));
624 CourseDialog *courseDialog = new CourseDialog(this);
625 courseDialog->init(course);
627 QString title = "Edit Course: " + course->getName();
628 courseDialog->setWindowTitle(title);
630 int result = courseDialog->exec();
632 QVector<QString> par(18);
633 QVector<QString> hcp(18);
634 QVector<QString> len(18);
636 courseDialog->results(par, hcp, len);
638 course->update(par, hcp, len);
639 saveClubFile(clubFile, clubList);
645 void MainWindow::deleteCourse()
649 Course * course = currentCourse();
652 qDebug() << "Invalid course for deletion";
655 club = course->parent();
657 // Can not delete course if it has scores -- check
658 if (findScore(club->getName(), course->getName()) != 0) {
659 showNote(tr("Can not delete course, delete scores on the course first"));
664 courseWindow->close();
666 club->delCourse(course);
668 if (club->isEmpty()) {
669 int index = clubList.indexOf(club);
671 clubList.removeAt(index);
675 saveClubFile(clubFile, clubList);
676 courseListModel->update(clubList);
680 void MainWindow::viewStatistics()
683 QMainWindow *win = new QMainWindow(this);
684 QString title = "Statistics";
685 win->setWindowTitle(title);
687 win->setAttribute(Qt::WA_Maemo5StackedWindow);
690 StatModel *model = new StatModel(clubList, scoreList);
692 QTableView *table = new QTableView;
694 table->setSelectionMode(QAbstractItemView::NoSelection);
695 table->setStyleSheet(statStyleSheet);
696 table->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
697 table->verticalHeader()->setResizeMode(QHeaderView::Stretch);
698 table->verticalHeader()->setAutoFillBackground(true);
699 table->setModel(model);
701 QWidget *central = new QWidget(win);
702 win->setCentralWidget(central);
704 QTextEdit *textEdit = new QTextEdit;
706 textEdit->setReadOnly(true);
708 QVBoxLayout *infoLayout = new QVBoxLayout;
709 infoLayout->addWidget(table);
711 QHBoxLayout *mainLayout = new QHBoxLayout(central);
712 mainLayout->addLayout(infoLayout);
713 central->setLayout(mainLayout);
718 void MainWindow::viewSettings()
721 SettingsDialog *dlg = new SettingsDialog(this);
723 dlg->init(conf, clubList);
725 int result = dlg->exec();
727 QString oldValue = conf.defaultCourses.toString();
729 QString newValue = conf.defaultCourses.toString();
732 // Reload club list, or drop r/o courses from list
733 if (oldValue == "Yes" && newValue == "No") {
734 flushReadOnlyItems();
735 courseListModel->update(clubList);
738 else if ((oldValue == "No" || oldValue == "") && newValue == "Yes") {
739 loadClubFile(masterFile, clubList, true);
740 courseListModel->update(clubList);
743 // TODO: do these only if needed
746 scoreListModel->update(scoreList);
751 void MainWindow::loadScoreFile(QString &fileName, QList<Score *> &list)
753 ScoreXmlHandler handler(list);
755 if (handler.parse(fileName))
756 qDebug() << "File loaded:" << fileName << " entries:" << list.size();
759 void MainWindow::saveScoreFile(QString &fileName, QList<Score *> &list)
761 ScoreXmlHandler handler(list);
763 if (handler.save(fileName))
765 qDebug() << "File saved:" << fileName << " entries:" << list.size();
767 qWarning() << "Unable to save:" << fileName;
770 void MainWindow::loadClubFile(QString &fileName, QList<Club *> &list, bool readOnly)
772 ClubXmlHandler handler(list);
774 if (handler.parse(fileName, readOnly))
775 qDebug() << "File loaded:" << fileName << " entries:" << list.size();
778 void MainWindow::saveClubFile(QString &fileName, QList<Club *> &list)
780 ClubXmlHandler handler(list);
782 if (handler.save(fileName))
784 qDebug() << "File saved:" << fileName << " entries:" << list.size();
786 qWarning() << "Unable to save:" << fileName;