SVN_SILENT: minor refactoring
[kdegames.git] / knetwalk / src / mainwindow.cpp
blob0e19f70cc3cce35e5b649f20546ddbfc694710e1
1 /*
2 Copyright 2004-2005 Andi Peredri <andi@ukr.net>
3 Copyright 2007 Simon Hürlimann <simon.huerlimann@huerlisi.ch>
4 Copyright 2007-2008 Fela Winkelmolen <fela.kde@gmail.com>
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "mainwindow.h"
22 #include <QEventLoop>
23 #include <QPushButton>
24 #include <QTimer>
25 #include <QPainter>
26 #include <QPixmap>
27 #include <QPaintEvent>
28 #include <QGridLayout>
29 #include <QCloseEvent>
30 #include <QApplication>
32 #include <KGlobal>
33 #include <KIconLoader>
34 #include <KLocale>
35 #include <KScoreDialog>
36 #include <KStandardAction>
37 #include <KAction>
38 #include <KActionCollection>
39 #include <KStandardGameAction>
40 #include <KStatusBar>
41 #include <KNotification>
42 #include <KNotifyConfigWidget>
43 #include <KStandardDirs>
44 #include <KSelectAction>
45 #include <KGameDifficulty>
47 #include <time.h>
49 #include "consts.h"
50 #include "settings.h"
51 #include "cell.h"
52 #include "view.h"
54 #include "renderer.h"
55 #include "abstractgrid.h"
57 static QMap<Directions, Directions> contrdirs;
59 MainWindow::MainWindow(QWidget *parent)
60 : KXmlGuiWindow(parent)
62 kDebug() << Settings::skill();
63 m_clickcount = 0;
65 contrdirs[Up] = Down;
66 contrdirs[Right] = Left;
67 contrdirs[Down] = Up;
68 contrdirs[Left] = Right;
70 setupActions();
72 statusBar()->insertPermanentItem("abcdefghijklmnopqrst: 0 ", 1, 1);
73 statusBar()->setItemAlignment(1, Qt::AlignLeft & Qt::AlignVCenter);
74 setAutoSaveSettings();
76 createBoard();
78 srand(time(0));
80 pixmapCache = new QPixmap(centralWidget()->size());
81 m_invalidCache = true;
83 // default values of KConfig XT don't seem to work
84 // this works around it. TODO: see why
86 // Difficulty
87 KGameDifficulty::init(this, this, SLOT(startNewGame()));
88 KGameDifficulty::addStandardLevel(KGameDifficulty::Easy);
89 KGameDifficulty::addStandardLevel(KGameDifficulty::Medium);
90 KGameDifficulty::addStandardLevel(KGameDifficulty::Hard);
91 KGameDifficulty::addStandardLevel(KGameDifficulty::VeryHard);
93 if (Settings::skill() == 0) {
94 KGameDifficulty::setLevel(KGameDifficulty::Easy);
95 } else {
96 KGameDifficulty::setLevel(
97 (KGameDifficulty::standardLevel) (Settings::skill()) );
99 kDebug() << KGameDifficulty::levelString() << Settings::skill();
101 setupGUI();
103 startNewGame();
106 MainWindow::~MainWindow()
108 delete pixmapCache;
111 void MainWindow::createBoard()
113 View* view = new View(this);
114 view->setFrameStyle(QFrame::NoFrame);
115 view->setMinimumSize(MinimumWidth, MinimumHeight);
117 gridLayout = new QGridLayout(view);
118 gridLayout->setMargin(0);
119 gridLayout->setSpacing(0);
120 setCentralWidget(view);
122 Cell::initPixmaps();
123 for (int i = 0; i < MasterBoardSize * MasterBoardSize; i++) {
124 board[i] = new Cell(view, i);
125 gridLayout->addWidget(board[i], i / MasterBoardSize, i % MasterBoardSize);
126 //board[i]->resize(32, 32); //TODO: needed ???
127 connect(board[i], SIGNAL(lClicked(int)), SLOT(lClicked(int)));
128 connect(board[i], SIGNAL(rClicked(int)), SLOT(rClicked(int)));
129 connect(board[i], SIGNAL(mClicked(int)), SLOT(mClicked(int)));
131 connect(board[i], SIGNAL(connectionsChanged()),
132 SLOT(updateConnections()));
134 board[i]->setWhatsThis(i18n("<h3>Rules of Game</h3><p>You are the "
135 "system administrator and your goal is to connect each terminal and "
136 "each cable to the central server.</p><p>Click the right mouse's "
137 "button for turning the cable in a clockwise direction, and left "
138 "mouse's button for turning the cable in a counter-clockwise "
139 "direction.</p><p>Start the LAN with as few turns as possible!</p>"));
143 void MainWindow::setBoardSize(int size)
145 if (!(size%2) || size > MasterBoardSize) {
146 kDebug() << "Wrong size!!\n";
147 return;
149 //TODO: boardSize = size
150 int start = (MasterBoardSize - size)/2;
151 for (int i = 0; i < start; ++i) {
152 // TODO: set only the last rows and columns to zero not half at the beginning and half at the end
153 gridLayout->setRowStretch(i, 0);
154 gridLayout->setRowStretch(MasterBoardSize - 1 - i, 0);
155 gridLayout->setColumnStretch(i, 0);
156 gridLayout->setColumnStretch(MasterBoardSize - 1 - i, 0);
159 for (int i = start; i < start + size; ++i) {
160 gridLayout->setRowStretch(i, 1);
161 gridLayout->setColumnStretch(i, 1);
165 void MainWindow::setupActions()
167 // Game
168 KStandardGameAction::gameNew(this, SLOT(startNewGame()),
169 actionCollection());
171 KStandardGameAction::highscores(this, SLOT(showHighscores()),
172 actionCollection());
174 KStandardGameAction::quit(this, SLOT(close()), actionCollection());
176 // Settings
177 KStandardAction::configureNotifications(this,
178 SLOT(configureNotifications()), actionCollection());
182 void MainWindow::showHighscores()
184 KScoreDialog ksdialog(KScoreDialog::Name, this);
185 ksdialog.exec();
188 void MainWindow::startNewGame()
190 gameEnded = false;
192 KGameDifficulty::standardLevel l = KGameDifficulty::level();
193 Settings::setSkill((int) l);
195 if (l == KGameDifficulty::VeryHard) {
196 wrapped = true;
197 } else {
198 wrapped = false;
201 Settings::self()->writeConfig();
203 m_clickcount = 0;
204 QString clicks = i18nc("Number of mouse clicks", "Moves: %1", m_clickcount);
205 statusBar()->changeItem(clicks, 1);
207 KNotification::event( "startsound", i18n("New Game") );
209 for (int i = 0; i < MasterBoardSize * MasterBoardSize; i++) {
210 board[i]->setDirs(None);
211 board[i]->setConnected(false);
212 board[i]->setRoot(false);
213 board[i]->setLocked(false);
216 const int size = boardSize();
217 setBoardSize(size);
219 // TODO: enable wrapped
220 AbstractGrid grid;
221 grid.initializeGrid(size, size, (Wrapping)wrapped);
223 const int start = (MasterBoardSize - size) / 2;
225 int i = 0; // index of grid
226 for (int r = start; r < start+size; ++r)
227 for (int c = start; c < start+size; ++c) {
228 int index = r * MasterBoardSize + c; // index of board
229 board[index]->setDirs(grid.cells()[i]->cables());
230 board[index]->setConnected(false);
231 board[index]->setRoot(false);
232 board[index]->setLocked(false);
234 if (grid.cells()[i]->isServer()) {
235 board[index]->setConnected(true); // TODO: put in Cell::setRoot()
236 board[index]->setRoot(true);
237 root = board[index];
239 ++i;
240 } // for for
242 const int start = (MasterBoardSize - size) / 2;
243 const int rootrow = rand() % size;
244 const int rootcol = rand() % size;
246 root = board[(start + rootrow) * MasterBoardSize + start + rootcol];
247 root->setConnected(true);
248 root->setRoot(true);
250 while (true)
252 for (int row = start; row < start + size; row++)
253 for(int col = start; col < start + size; col++)
254 board[row * MasterBoardSize + col]->setDirs(None);
256 CellList list;
257 list.append(root);
258 if (rand() % 2) addRandomDir(list);
260 while (!list.isEmpty())
262 if(rand() % 2)
264 addRandomDir(list);
265 if (rand() % 2) addRandomDir(list);
266 list.erase(list.begin());
268 else
270 list.append(list.first());
271 list.erase(list.begin());
275 int cells = 0;
276 for (int i = 0; i < MasterBoardSize * MasterBoardSize; i++)
278 Directions d = board[i]->dirs();
279 if ((d != None) && (d != Cell::None)) cells++;
281 if (cells >= MinimumNumCells) break;
284 for (int i = 0; i < MasterBoardSize * MasterBoardSize; i++)
285 board[i]->rotate((rand() % 4) * 90);
287 updateConnections();
288 KGameDifficulty::setRunning(false); // setRunning(true) on the first click
291 void MainWindow::updateConnections()
293 bool newconnection[MasterBoardSize * MasterBoardSize];
294 for (int i = 0; i < MasterBoardSize * MasterBoardSize; i++) {
295 newconnection[i] = false;
298 CellList list;
299 if (!root->isRotated()) {
300 newconnection[root->index()] = true;
301 list.append(root);
304 while (!list.isEmpty()) {
305 Cell* cell = list.first();
306 Cell* ucell = uCell(cell);
307 Cell* rcell = rCell(cell);
308 Cell* dcell = dCell(cell);
309 Cell* lcell = lCell(cell);
311 if ((cell->dirs() & Up) && ucell && (ucell->dirs() & Down) &&
312 !newconnection[ucell->index()] && !ucell->isRotated()) {
313 newconnection[ucell->index()] = true;
314 list.append(ucell);
316 if ((cell->dirs() & Right) && rcell && (rcell->dirs() & Left) &&
317 !newconnection[rcell->index()] && !rcell->isRotated()) {
318 newconnection[rcell->index()] = true;
319 list.append(rcell);
321 if ((cell->dirs() & Down) && dcell && (dcell->dirs() & Up) &&
322 !newconnection[dcell->index()] && !dcell->isRotated()) {
323 newconnection[dcell->index()] = true;
324 list.append(dcell);
326 if ((cell->dirs() & Left) && lcell && (lcell->dirs() & Right) &&
327 !newconnection[lcell->index()] && !lcell->isRotated()) {
328 newconnection[lcell->index()] = true;
329 list.append(lcell);
331 list.erase(list.begin());
334 bool newConnections = false;
335 for (int i = 0; i < MasterBoardSize * MasterBoardSize; i++) {
336 if (!board[i]->isConnected() && newconnection[i]) {
337 newConnections = true;
339 board[i]->setConnected(newconnection[i]);
342 if (newConnections) checkIfGameEnded();
345 Cell* MainWindow::uCell(Cell* cell) const
347 if (cell->index() >= MasterBoardSize) {
348 return board[cell->index() - MasterBoardSize];
349 } else if (wrapped) {
350 return board[MasterBoardSize * (MasterBoardSize - 1) + cell->index()];
351 } else {
352 return 0;
356 Cell* MainWindow::dCell(Cell* cell) const
358 if (cell->index() < MasterBoardSize * (MasterBoardSize - 1)) {
359 return board[cell->index() + MasterBoardSize];
360 } else if (wrapped) {
361 return board[cell->index() - MasterBoardSize * (MasterBoardSize - 1)];
362 } else {
363 return 0;
367 Cell* MainWindow::lCell(Cell* cell) const
369 if (cell->index() % MasterBoardSize > 0) {
370 return board[cell->index() - 1];
371 } else if (wrapped) {
372 return board[cell->index() - 1 + MasterBoardSize];
373 } else {
374 return 0;
378 Cell* MainWindow::rCell(Cell* cell) const
380 if (cell->index() % MasterBoardSize < MasterBoardSize - 1) {
381 return board[cell->index() + 1];
382 } else if (wrapped) {
383 return board[cell->index() + 1 - MasterBoardSize];
384 } else {
385 return 0;
389 void MainWindow::lClicked(int index)
391 KGameDifficulty::setRunning(true);
392 rotate(index, false);
395 void MainWindow::rClicked(int index)
397 KGameDifficulty::setRunning(true);
398 rotate(index, true);
401 void MainWindow::mClicked(int index)
403 KGameDifficulty::setRunning(true);
404 board[index]->setLocked( !board[index]->isLocked() );
407 void MainWindow::rotate(int index, bool clockWise)
409 const Directions d = board[index]->dirs();
410 if ((d == None) || gameEnded || board[index]->isLocked()) {
411 KNotification::event( "clicksound" );
412 //blink(index);
413 } else {
414 KNotification::event( "turnsound" );
416 board[index]->animateRotation(clockWise);
418 // FIXME: won't work!!!
419 //if (updateConnections())
420 // KNotification::event( "connectsound" );
422 m_clickcount++;
423 QString clicks = i18n("Moves: %1",m_clickcount);
424 statusBar()->changeItem(clicks,1);
428 void MainWindow::blink(int index)
430 for (int i = 0; i < board[index]->width() * 2; i += 2) {
431 qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
432 QTimer::singleShot(30, board[index], SLOT(update()));
433 qApp->processEvents(QEventLoop::ExcludeUserInputEvents |
434 QEventLoop::WaitForMoreEvents);
435 board[index]->setLight(i);
438 board[index]->setLight(0);
441 void MainWindow::checkIfGameEnded()
443 bool ended = true;
444 for (int i = 0; i < MasterBoardSize * MasterBoardSize; i++) {
445 const Directions d = board[i]->dirs();
446 if ((d != None) && !board[i]->isConnected())
447 ended = false;
450 if (ended) {
451 KNotification::event( "winsound" );
452 //blink(index);
454 KScoreDialog ksdialog(KScoreDialog::Name, this);
455 ksdialog.setConfigGroup(KGameDifficulty::levelString());
457 //ksdialog.addField(KScoreDialog::Custom1, "Num of Moves", "moves");
458 //KScoreDialog::FieldInfo scoreInfo;
459 //scoreInfo[KScoreDialog::Score].setNum(1000 * boardSize() * boardSize() / m_clickcount);
460 //scoreInfo[KScoreDialog::Score].setNum(m_clickcount);
462 ksdialog.addScore(m_clickcount, KScoreDialog::LessIsMore);
463 ksdialog.exec();
465 KGameDifficulty::setRunning(false);
466 gameEnded = true;
470 int MainWindow::boardSize()
472 switch (KGameDifficulty::level()) {
473 case KGameDifficulty::Easy: return NoviceBoardSize;
474 case KGameDifficulty::Medium: return NormalBoardSize;
475 case KGameDifficulty::Hard: return ExpertBoardSize;
476 default: return MasterBoardSize;
480 void MainWindow::closeEvent(QCloseEvent* event)
482 event->accept();
485 void MainWindow::configureNotifications()
487 KNotifyConfigWidget::configure(this);
490 void MainWindow::paintEvent(QPaintEvent* e)
492 if (e->rect().intersects(centralWidget()->geometry())) {
493 QPainter painter;
494 QRect updateRect = e->rect().intersect(centralWidget()->geometry());
496 if (m_invalidCache) {
497 m_invalidCache = false;
499 // keep aspect ratio
500 int width = centralWidget()->width();
501 int height = centralWidget()->height();
503 delete pixmapCache;
504 pixmapCache = new QPixmap(width, height);
505 *pixmapCache =
506 Renderer::self()->backgroundPixmap(centralWidget()->size());
508 // calculate background overlay bounding rect
509 int size = qMin(width, height);
511 // add a border
512 size = static_cast<int>(size * (1.0 - 2*OverlayBorder));
513 int borderLeft = (width - size)/2;
514 int borderTop = (height - size)/2;
516 QPixmap overlay = Renderer::self()->backgroundOverlayPixmap(size);
518 painter.begin(pixmapCache);
519 painter.drawPixmap(borderLeft, borderTop, overlay);
520 painter.end();
523 QPoint p = centralWidget()->mapFromParent(QPoint(updateRect.x(),
524 updateRect.y()));
525 QRect pixmapRect(p.x(), p.y(), updateRect.width(), updateRect.height());
526 painter.begin(this);
528 painter.drawPixmap(updateRect, *pixmapCache, pixmapRect);
529 painter.end();
533 void MainWindow::resizeEvent(QResizeEvent*)
535 m_invalidCache = true;
538 #include "mainwindow.moc"