1 /***********************************************************************
2  *
3  * Copyright (C) 2008, 2009, 2012, 2014, 2015 Graeme Gott <graeme@gottcode.org>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  ***********************************************************************/
19 
20 #include "scores.h"
21 
22 #include <QDialogButtonBox>
23 #include <QHeaderView>
24 #include <QInputDialog>
25 #include <QLabel>
26 #include <QScrollBar>
27 #include <QSettings>
28 #include <QTime>
29 #include <QTreeWidget>
30 #include <QVBoxLayout>
31 
32 #if defined(Q_OS_UNIX)
33 #include <pwd.h>
34 #include <unistd.h>
35 
36 #elif defined(Q_OS_WIN)
37 #include <lmcons.h>
38 #include <windows.h>
39 
40 #endif
41 
42 namespace {
43 // ============================================================================
44 
45 class Score : public QTreeWidgetItem
46 {
47 public:
48 	Score(int seconds, int steps, int algorithm, int size);
49 
operator <(const QTreeWidgetItem & other) const50 	bool operator<(const QTreeWidgetItem& other) const
51 	{
52 		return data(1, Qt::UserRole).toInt() < other.data(1, Qt::UserRole).toInt();
53 	}
54 };
55 
56 // ============================================================================
57 
Score(int seconds,int steps,int algorithm,int size)58 Score::Score(int seconds, int steps, int algorithm, int size)
59 :	QTreeWidgetItem(QTreeWidgetItem::UserType + 1)
60 {
61 	int score = seconds ? ((steps * size) / seconds) : (steps * size);
62 	setText(1, QString::number(score));
63 	setData(1, Qt::UserRole, score);
64 	setText(2, QTime(0, 0, 0).addSecs(seconds).toString("hh:mm:ss"));
65 	setData(2, Qt::UserRole, QString::number(seconds));
66 	setText(3, QString::number(steps));
67 	setData(4, Qt::UserRole, QString::number(algorithm));
68 	setText(5, QString::number(size));
69 
70 	setTextAlignment(1, Qt::AlignRight | Qt::AlignVCenter);
71 	setTextAlignment(2, Qt::AlignRight | Qt::AlignVCenter);
72 	setTextAlignment(3, Qt::AlignRight | Qt::AlignVCenter);
73 	setTextAlignment(4, Qt::AlignCenter | Qt::AlignVCenter);
74 	setTextAlignment(5, Qt::AlignRight | Qt::AlignVCenter);
75 
76 	static QStringList algorithms = QStringList()
77 		<< QLabel::tr("Hunt and Kill")
78 		<< QLabel::tr("Kruskal")
79 		<< QLabel::tr("Prim")
80 		<< QLabel::tr("Recursive Backtracker")
81 		<< QLabel::tr("Stack")
82 		<< QLabel::tr("Stack 2")
83 		<< QLabel::tr("Stack 3")
84 		<< QLabel::tr("Stack 4")
85 		<< QLabel::tr("Stack 5");
86 	Q_ASSERT(algorithm > -1);
87 	Q_ASSERT(algorithm < algorithms.size());
88 	setText(4, algorithms.at(algorithm));
89 }
90 
91 // ============================================================================
92 }
93 
94 // ============================================================================
95 
Scores(QWidget * parent)96 Scores::Scores(QWidget* parent)
97 :	QDialog(parent)
98 {
99 	setWindowTitle(tr("CuteMaze Scores"));
100 
101 	QVBoxLayout* layout = new QVBoxLayout(this);
102 
103 	m_board = new QTreeWidget(this);
104 	m_board->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
105 	m_board->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
106 	m_board->setRootIsDecorated(false);
107 	m_board->setColumnCount(5);
108 	m_board->setHeaderLabels(QStringList() << tr("Name") << tr("Score") << tr("Time") << tr("Steps") << tr("Algorithm") << tr("Size"));
109 	m_board->header()->setStretchLastSection(false);
110 	m_board->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
111 	layout->addWidget(m_board);
112 
113 	layout->addSpacing(12);
114 
115 	QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, this);
116 	connect(buttons, &QDialogButtonBox::rejected, this, &Scores::reject);
117 	layout->addWidget(buttons);
118 
119 	read();
120 }
121 
122 // ============================================================================
123 
addScore(int steps,int seconds,int algorithm,int size)124 void Scores::addScore(int steps, int seconds, int algorithm, int size)
125 {
126 	// Check if it is a high score
127 	int value = seconds ? ((steps * size) / seconds) : (steps * size);
128 	int count = m_board->topLevelItemCount();
129 	Q_ASSERT(count < 11);
130 	if (count == 10 && value < m_board->topLevelItem(count - 1)->text(1).toInt()) {
131 		return;
132 	}
133 
134 	// Get player's name
135 	QString name;
136 #if defined(Q_OS_UNIX)
137 	{
138 		passwd* pws = getpwuid(geteuid());
139 		if (pws) {
140 			name = pws->pw_gecos;
141 			if (name.isEmpty()) {
142 				name = pws->pw_name;
143 			}
144 		}
145 	}
146 #elif defined(Q_OS_WIN)
147 	{
148 		WCHAR buffer[UNLEN + 1];
149 		DWORD count = sizeof(buffer);
150 		if (GetUserName(buffer, &count)) {
151 			name = QString::fromStdWString(buffer);
152 		}
153 	}
154 #endif
155 	bool ok = true;
156 	name = QInputDialog::getText(parentWidget(), tr("Congratulations!"), tr("Your score has made the top ten.\nPlease enter your name:"), QLineEdit::Normal, name, &ok);
157 	if (!ok || name.isEmpty()) {
158 		return;
159 	}
160 
161 	// Create score
162 	QTreeWidgetItem* score = new Score(seconds, steps, algorithm, size);
163 	score->setText(0, name);
164 	m_board->addTopLevelItem(score);
165 	m_board->clearSelection();
166 	score->setSelected(true);
167 	updateItems();
168 
169 	// Save items
170 	QStringList values;
171 	QTreeWidgetItem* item;
172 	count = m_board->topLevelItemCount();
173 	for (int i = 0; i < count; ++i) {
174 		item = m_board->topLevelItem(i);
175 		values += QString("%1:%2:%3:%4:%5") .arg(item->text(0)) .arg(item->data(2, Qt::UserRole).toInt()) .arg(item->text(3)) .arg(item->data(4, Qt::UserRole).toInt()) .arg(item->text(5).toInt());
176 	}
177 	QSettings().setValue("Scores", values);
178 
179 	show();
180 }
181 
182 // ============================================================================
183 
read()184 void Scores::read()
185 {
186 	QStringList data = QSettings().value("Scores").toStringList();
187 	for (const QString& s : data) {
188 		QStringList values = s.split(':');
189 		if (values.size() != 5) {
190 			continue;
191 		}
192 		Score* score = new Score(values[1].toInt(), values[2].toInt(), values[3].toInt(), values[4].toInt());
193 		score->setText(0, values[0]);
194 		m_board->addTopLevelItem(score);
195 	}
196 	updateItems();
197 }
198 
199 // ============================================================================
200 
updateItems()201 void Scores::updateItems()
202 {
203 	// Sort items
204 	m_board->sortItems(1, Qt::DescendingOrder);
205 	while (m_board->topLevelItemCount() > 10) {
206 		m_board->takeTopLevelItem(10);
207 	}
208 
209 	// Find minimum size
210 	int frame = m_board->frameWidth() * 2;
211 	int width = frame;
212 	for (int i = 0; i < 6; ++i) {
213 		width += m_board->columnWidth(i);
214 	}
215 	m_board->setMinimumSize(width, frame + m_board->header()->height() + m_board->sizeHintForRow(0) * 10);
216 }
217 
218 // ============================================================================
219