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/.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.defaultCourses = settings.value(settingsDefaultCourses);
281 // Use default courses if no settings for that
282 if (!conf.defaultCourses.isValid())
283 conf.defaultCourses = "Yes";
285 // Use date sort order if no settings for that
286 if (!conf.sortOrder.isValid())
287 conf.sortOrder = "Date";
289 qDebug() << "Settings: " << conf.hcp << conf.homeClub << conf.sortOrder << conf.defaultCourses;
292 void MainWindow::saveSettings(void)
295 settings.beginGroup(settingsGroup);
296 if (conf.hcp.isValid())
297 settings.setValue(settingsHcp, conf.hcp);
298 if (conf.homeClub.isValid())
299 settings.setValue(settingsHomeClub, conf.homeClub);
300 if (conf.sortOrder.isValid())
301 settings.setValue(settingsSortOrder, conf.sortOrder);
302 if (conf.defaultCourses.isValid())
303 settings.setValue(settingsDefaultCourses, conf.defaultCourses);
307 void MainWindow::createLayoutList(QWidget *parent)
310 QVBoxLayout * tableLayout = new QVBoxLayout;
311 tableLayout->addWidget(list);
313 QHBoxLayout *mainLayout = new QHBoxLayout(parent);
314 mainLayout->addLayout(tableLayout);
315 parent->setLayout(mainLayout);
318 void MainWindow::createListView(QList<Score *> &scoreList,
319 QList <Club *> &clubList)
322 list = new QListView(this);
324 scoreListModel = new ScoreListModel(scoreList, clubList);
325 courseListModel = new CourseListModel(clubList);
327 list->setStyleSheet(defaultStyleSheet);
329 list->setSelectionMode(QAbstractItemView::SingleSelection);
330 list->setProperty("FingerScrolling", true);
335 connect(list, SIGNAL(clicked(QModelIndex)),
336 this, SLOT(clickedList(QModelIndex)));
339 void MainWindow::listScores()
342 list->setModel(scoreListModel);
343 selectionModel = list->selectionModel();
344 updateTitleBar(titleScores);
347 void MainWindow::listCourses()
350 list->setModel(courseListModel);
351 selectionModel = list->selectionModel();
352 updateTitleBar(titleCourses);
355 void MainWindow::createActions()
358 newScoreAction = new QAction(tr("New Score"), this);
359 connect(newScoreAction, SIGNAL(triggered()), this, SLOT(newScore()));
361 newCourseAction = new QAction(tr("New Course"), this);
362 connect(newCourseAction, SIGNAL(triggered()), this, SLOT(newCourse()));
364 statAction = new QAction(tr("Statistics"), this);
365 connect(statAction, SIGNAL(triggered()), this, SLOT(viewStatistics()));
367 settingsAction = new QAction(tr("Settings"), this);
368 connect(settingsAction, SIGNAL(triggered()), this, SLOT(viewSettings()));
370 // Maemo5 style menu filters
371 filterGroup = new QActionGroup(this);
372 filterGroup->setExclusive(true);
374 listScoreAction = new QAction(tr("Scores"), filterGroup);
375 listScoreAction->setCheckable(true);
376 listScoreAction->setChecked(true);
377 connect(listScoreAction, SIGNAL(triggered()), this, SLOT(listScores()));
379 listCourseAction = new QAction(tr("Courses"), filterGroup);
380 listCourseAction->setCheckable(true);
381 connect(listCourseAction, SIGNAL(triggered()), this, SLOT(listCourses()));
384 void MainWindow::createMenus()
388 menu = menuBar()->addMenu("");
390 menu = menuBar()->addMenu("Menu");
393 menu->addAction(newScoreAction);
394 menu->addAction(newCourseAction);
395 menu->addAction(statAction);
396 menu->addAction(settingsAction);
397 menu->addActions(filterGroup->actions());
400 void MainWindow::updateTitleBar(QString & msg)
406 void MainWindow::showNote(QString msg)
409 QMaemo5InformationBox::information(this,
411 QMaemo5InformationBox::DefaultTimeout);
415 void MainWindow::clickedList(const QModelIndex &index)
418 int row = index.row();
420 const QAbstractItemModel *m = index.model();
421 if (m == scoreListModel) {
422 if (row < scoreList.count()) {
423 Score * score = scoreList.at(row);
424 Course * course = findCourse(score->getClubName(), score->getCourseName());
425 viewScore(score, course);
428 else if (m == courseListModel) {
429 QString str = courseListModel->data(index, Qt::DisplayRole).toString();
430 QStringList strList = str.split(", ");
432 if (strList.count() != 2) {
433 showNote(QString("Invalid course selection"));
436 Course * course = findCourse(strList.at(0), strList.at(1));
441 void MainWindow::viewScore(Score * score, Course * course)
444 scoreWindow->setup(score, course);
448 void MainWindow::newScore()
451 SelectDialog *selectDialog = new SelectDialog(this);
453 selectDialog->init(clubList);
455 int result = selectDialog->exec();
461 selectDialog->results(clubName, courseName, date);
463 ScoreDialog *scoreDialog = new ScoreDialog(this);
464 QString title = "New Score: " + courseName + ", " + date;
465 scoreDialog->setWindowTitle(title);
467 Club *club = findClub(clubName);
469 showNote(tr("Error: no such club"));
472 Course *course = club->getCourse(courseName);
474 showNote(tr("Error: no such course:"));
477 scoreDialog->init(course);
478 result = scoreDialog->exec();
480 QVector<QString> scores(18);
482 scoreDialog->results(scores);
483 Score *score = new Score(scores, clubName, courseName, date);
486 // Sort the scores based on settings
489 saveScoreFile(scoreFile, scoreList);
490 scoreListModel->update(scoreList);
496 void MainWindow::editScore()
500 Score *score = currentScore();
503 course = findCourse(score->getClubName(), score->getCourseName());
505 if (!course || !score) {
506 qDebug() << "No score/course to edit";
510 QString date = score->getDate();
512 ScoreDialog *scoreDialog = new ScoreDialog(this);
513 scoreDialog->init(course, score);
515 QString title = "Edit Score: " + course->getName() + ", " + date;
516 scoreDialog->setWindowTitle(title);
518 int result = scoreDialog->exec();
520 QVector<QString> scores(18);
522 scoreDialog->results(scores);
524 score->update(scores);
526 // Sort the scores based on dates
527 qSort(scoreList.begin(), scoreList.end(), dateMoreThan);
529 saveScoreFile(scoreFile, scoreList);
535 void MainWindow::deleteScore()
539 scoreWindow->close();
541 QModelIndex index = selectionModel->currentIndex();
542 if (!index.isValid()) {
543 qDebug() << "Invalid index";
547 scoreList.removeAt(index.row());
549 saveScoreFile(scoreFile, scoreList);
550 scoreListModel->update(scoreList);
554 void MainWindow::viewCourse(Course * course)
557 courseWindow->setup(course);
558 courseWindow->show();
561 void MainWindow::newCourse()
564 CourseSelectDialog *selectDialog = new CourseSelectDialog(this);
566 int result = selectDialog->exec();
572 selectDialog->results(clubName, courseName);
574 CourseDialog *courseDialog = new CourseDialog(this);
575 courseDialog->init();
576 QString title = "New Course: " + clubName + "," + courseName;
577 courseDialog->setWindowTitle(title);
579 int result = courseDialog->exec();
581 QVector<QString> par(18);
582 QVector<QString> hcp(18);
583 QVector<QString> len(18);
585 courseDialog->results(par, hcp, len);
588 Club *club = findClub(clubName);
590 course = club->getCourse(courseName);
592 showNote(tr("Club/course already in the database"));
596 course = new Course(courseName, par, hcp);
597 club->addCourse(course);
601 // New club and course
602 club = new Club(clubName);
603 course = new Course(courseName, par, hcp);
604 club->addCourse(course);
608 saveClubFile(clubFile, clubList);
609 courseListModel->update(clubList);
615 void MainWindow::editCourse()
618 Course *course = currentCourse();
621 showNote(tr("No course on edit"));
625 CourseDialog *courseDialog = new CourseDialog(this);
626 courseDialog->init(course);
628 QString title = "Edit Course: " + course->getName();
629 courseDialog->setWindowTitle(title);
631 int result = courseDialog->exec();
633 QVector<QString> par(18);
634 QVector<QString> hcp(18);
635 QVector<QString> len(18);
637 courseDialog->results(par, hcp, len);
639 course->update(par, hcp, len);
640 saveClubFile(clubFile, clubList);
646 void MainWindow::deleteCourse()
650 Course * course = currentCourse();
653 qDebug() << "Invalid course for deletion";
656 club = course->parent();
658 // Can not delete course if it has scores -- check
659 if (findScore(club->getName(), course->getName()) != 0) {
660 showNote(tr("Can not delete course, delete scores on the course first"));
665 courseWindow->close();
667 club->delCourse(course);
669 if (club->isEmpty()) {
670 int index = clubList.indexOf(club);
672 clubList.removeAt(index);
676 saveClubFile(clubFile, clubList);
677 courseListModel->update(clubList);
681 void MainWindow::viewStatistics()
684 QMainWindow *win = new QMainWindow(this);
685 QString title = "Statistics";
686 win->setWindowTitle(title);
688 win->setAttribute(Qt::WA_Maemo5StackedWindow);
691 StatModel *model = new StatModel(clubList, scoreList);
693 QTableView *table = new QTableView;
695 table->setSelectionMode(QAbstractItemView::NoSelection);
696 table->setStyleSheet(statStyleSheet);
697 table->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
698 table->verticalHeader()->setResizeMode(QHeaderView::Stretch);
699 table->verticalHeader()->setAutoFillBackground(true);
700 table->setModel(model);
702 QWidget *central = new QWidget(win);
703 win->setCentralWidget(central);
705 QTextEdit *textEdit = new QTextEdit;
707 textEdit->setReadOnly(true);
709 QVBoxLayout *infoLayout = new QVBoxLayout;
710 infoLayout->addWidget(table);
712 QHBoxLayout *mainLayout = new QHBoxLayout(central);
713 mainLayout->addLayout(infoLayout);
714 central->setLayout(mainLayout);
719 void MainWindow::viewSettings()
722 SettingsDialog *dlg = new SettingsDialog(this);
724 dlg->init(conf, clubList);
726 int result = dlg->exec();
728 QString oldValue = conf.defaultCourses.toString();
730 QString newValue = conf.defaultCourses.toString();
733 // Reload club list, or drop r/o courses from list
734 if (oldValue == "Yes" && newValue == "No") {
735 flushReadOnlyItems();
736 courseListModel->update(clubList);
739 else if ((oldValue == "No" || oldValue == "") && newValue == "Yes") {
740 loadClubFile(masterFile, clubList, true);
741 courseListModel->update(clubList);
744 // TODO: do these only if needed
747 scoreListModel->update(scoreList);
752 void MainWindow::loadScoreFile(QString &fileName, QList<Score *> &list)
754 ScoreXmlHandler handler(list);
756 if (handler.parse(fileName))
757 qDebug() << "File loaded:" << fileName << " entries:" << list.size();
760 void MainWindow::saveScoreFile(QString &fileName, QList<Score *> &list)
762 ScoreXmlHandler handler(list);
764 if (handler.save(fileName))
766 qDebug() << "File saved:" << fileName << " entries:" << list.size();
768 qWarning() << "Unable to save:" << fileName;
771 void MainWindow::loadClubFile(QString &fileName, QList<Club *> &list, bool readOnly)
773 ClubXmlHandler handler(list);
775 if (handler.parse(fileName, readOnly))
776 qDebug() << "File loaded:" << fileName << " entries:" << list.size();
779 void MainWindow::saveClubFile(QString &fileName, QList<Club *> &list)
781 ClubXmlHandler handler(list);
783 if (handler.save(fileName))
785 qDebug() << "File saved:" << fileName << " entries:" << list.size();
787 qWarning() << "Unable to save:" << fileName;