1 /*
2 SPDX-FileCopyrightText: 2003 Marco Krüger <grisuji@gmx.de>
3 SPDX-FileCopyrightText: 2003, 2009 Ian Wadham <iandw.au@gmail.com>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "kgrglobals.h"
9
10 #include "kgrselector.h"
11
12 #include "kgrgameio.h"
13
14 #include <QApplication>
15 #include <QDesktopWidget>
16 #include <QGridLayout>
17 #include <QHeaderView>
18 #include <QLabel>
19 #include <QPainter>
20 #include <QPushButton>
21 #include <QScrollBar>
22 #include <QSpacerItem>
23 #include <QSpinBox>
24 #include <QTextEdit>
25 #include <QVBoxLayout>
26 #include <QDialogButtonBox>
27 #include <KGuiItem>
28 #include <KConfigGroup>
29 #include <KSharedConfig>
30 #include <KLocalizedString>
31 /******************************************************************************/
32 /***************** DIALOG BOX TO SELECT A GAME AND LEVEL *****************/
33 /******************************************************************************/
34
KGrSLDialog(int action,int requestedLevel,int gameIndex,QList<KGrGameData * > & gameList,const QString & pSystemDir,const QString & pUserDir,QWidget * parent)35 KGrSLDialog::KGrSLDialog (int action, int requestedLevel, int gameIndex,
36 QList<KGrGameData *> & gameList,
37 const QString & pSystemDir, const QString & pUserDir,
38 QWidget * parent)
39 :
40 QDialog (parent),
41 slAction (action),
42 myGameList (gameList),
43 defaultGame (gameIndex),
44 defaultLevel (requestedLevel),
45 systemDir (pSystemDir),
46 userDir (pUserDir),
47 slParent (parent)
48 {
49 setupWidgets();
50 }
51
~KGrSLDialog()52 KGrSLDialog::~KGrSLDialog()
53 {
54 }
55
selectLevel(int & selectedGame,int & selectedLevel)56 bool KGrSLDialog::selectLevel (int & selectedGame, int & selectedLevel)
57 {
58 selectedGame = defaultGame;
59 selectedLevel = 0; // 0 = no selection (Cancel) or invalid.
60
61 // Create and run a modal dialog box to select a game and level.
62 while (exec() == QDialog::Accepted) {
63 selectedGame = slGameIndex;
64 selectedLevel = 0; // In case the selection is invalid.
65 if (myGameList.at (selectedGame)->owner == SYSTEM) {
66 switch (slAction) {
67 case SL_CREATE: // Can save only in a USER collection.
68 case SL_SAVE:
69 case SL_MOVE:
70 KGrMessage::information (slParent, i18n ("Select Level"),
71 i18n ("Sorry, you can only save or move "
72 "into one of your own games."));
73 continue; // Re-run the dialog box.
74 break;
75 case SL_DELETE: // Can delete only in a USER collection.
76 KGrMessage::information (slParent, i18n ("Select Level"),
77 i18n ("Sorry, you can only delete a level "
78 "from one of your own games."));
79 continue; // Re-run the dialog box.
80 break;
81 case SL_UPD_GAME: // Can edit info only in a USER collection.
82 KGrMessage::information (slParent, i18n ("Edit Game Info"),
83 i18n ("Sorry, you can only edit the game "
84 "information on your own games."));
85 continue; // Re-run the dialog box.
86 break;
87 default:
88 break;
89 }
90 }
91
92 selectedLevel = number->value();
93 if ((selectedLevel > myGameList.at (selectedGame)->nLevels) &&
94 (slAction != SL_CREATE) && (slAction != SL_SAVE) &&
95 (slAction != SL_MOVE) && (slAction != SL_UPD_GAME)) {
96 KGrMessage::information (slParent, i18n ("Select Level"),
97 i18n ("There is no level %1 in \"%2\", "
98 "so you cannot play or edit it.",
99 selectedLevel,
100 myGameList.at (selectedGame)->name));
101 selectedLevel = 0; // Set an invalid selection.
102 continue; // Re-run the dialog box.
103 }
104 break; // Accepted and valid.
105 }
106 return (selectedLevel > 0); // 0 = cancelled or invalid.
107 }
108
setupWidgets()109 void KGrSLDialog::setupWidgets()
110 {
111 int margin = 0;
112 int spacing = 0;
113 QWidget * dad = new QWidget (this);
114
115 QVBoxLayout *layout = new QVBoxLayout;
116 setLayout(layout);
117 layout->addWidget(dad);
118
119 setWindowTitle (i18nc("@title:window", "Select Game"));
120 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help);
121 QVBoxLayout *buttonLayout = new QVBoxLayout;
122 connect(buttonBox, &QDialogButtonBox::accepted, this, &KGrSLDialog::accept);
123 connect(buttonBox, &QDialogButtonBox::rejected, this, &KGrSLDialog::reject);
124 buttonLayout->addWidget(buttonBox);
125 buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
126 layout->addWidget(buttonBox);
127
128 QVBoxLayout * mainLayout = new QVBoxLayout (dad);
129 mainLayout->setSpacing (spacing);
130 mainLayout->setContentsMargins(margin, margin, margin, margin);
131
132 gameL = new QLabel
133 (i18n ("<html><b>Please select a game:</b></html>"), dad);
134 mainLayout->addWidget (gameL, 5);
135
136 games = new QTreeWidget (dad);
137 mainLayout->addWidget(games);
138 mainLayout->addWidget (games, 50);
139 games->setColumnCount (4);
140 games->setHeaderLabels (QStringList() <<
141 i18n ("Name of Game") <<
142 i18n ("Rules") <<
143 i18n ("Levels") <<
144 i18n ("Skill"));
145 games->setRootIsDecorated (false);
146
147 QHBoxLayout * hboxLayout1 = new QHBoxLayout();
148 hboxLayout1->setSpacing (6);
149 hboxLayout1->setContentsMargins(0, 0, 0, 0);
150
151 gameN = new QLabel (dad); // Name of selected game.
152 QFont f = gameN->font();
153 f.setBold (true);
154 gameN->setFont (f);
155 hboxLayout1->addWidget (gameN);
156
157 QSpacerItem * spacerItem1 = new QSpacerItem
158 (21, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
159 hboxLayout1->addItem (spacerItem1);
160
161 gameD = new QLabel (dad); // Description of game.
162 hboxLayout1->addWidget (gameD);
163 mainLayout->addLayout (hboxLayout1, 5);
164
165 gameAbout = new QTextEdit (dad);
166 gameAbout->setReadOnly (true);
167 mainLayout->addWidget (gameAbout, 25);
168
169 QFrame * separator = new QFrame (dad);
170 separator->setFrameShape (QFrame::HLine);
171 mainLayout->addWidget (separator);
172
173 if ((slAction == SL_START) || (slAction == SL_UPD_GAME)) {
174 dad-> setWindowTitle (i18nc("@title:window", "Select Game"));
175 QLabel * startMsg = new QLabel
176 (QStringLiteral("<b>") + i18n ("Level 1 of the selected game is:") + QStringLiteral("</b>"), dad);
177 mainLayout->addWidget (startMsg, 5);
178 }
179 else {
180 dad-> setWindowTitle (i18nc("@title:window", "Select Game/Level"));
181 QLabel * selectLev = new QLabel
182 (QStringLiteral("<b>") + i18n ("Please select a level:") + QStringLiteral("</b>"), dad);
183 mainLayout->addWidget (selectLev, 5);
184 }
185
186 QGridLayout * grid = new QGridLayout;
187 mainLayout->addLayout (grid);
188
189 number = new QScrollBar (Qt::Vertical, dad);
190 number->setRange (1, 150);
191 number->setSingleStep (1);
192 number->setPageStep (10);
193 number->setValue (1);
194 grid->addWidget (number, 1, 5, 4, 1);
195
196 QWidget * numberPair = new QWidget (dad);
197 QHBoxLayout *hboxLayout2 = new QHBoxLayout (numberPair);
198 hboxLayout2->setContentsMargins(0, 0, 0, 0);
199 numberPair->setLayout (hboxLayout2);
200 grid->addWidget (numberPair, 1, 1, 1, 3);
201 numberL = new QLabel (i18n ("Level number:"), numberPair);
202 display = new QSpinBox (numberPair);
203 display->setRange (1, 150);
204 hboxLayout2->addWidget (numberL);
205 hboxLayout2->addWidget (display);
206
207 levelNH = new QPushButton (i18n ("Edit Level Name o&r Hint"), dad);
208 mainLayout->addWidget (levelNH);
209
210 slName = new QLabel (dad);
211 grid->addWidget (slName, 2, 1, 1, 4);
212 thumbNail = new KGrThumbNail (dad);
213 grid->addWidget (thumbNail, 1, 6, 4, 5);
214
215 // Set thumbnail cell size to about 1/5 of game cell size.
216 int cellSize = slParent->width() / (5 * (FIELDWIDTH + 4));
217 cellSize = (cellSize < 4) ? 4 : cellSize;
218 thumbNail-> setFixedWidth ((FIELDWIDTH * cellSize) + 2);
219 thumbNail-> setFixedHeight ((FIELDHEIGHT * cellSize) + 2);
220
221 // Base the geometry of the dialog box on the playing area.
222 int cell = slParent->width() / (FIELDWIDTH + 4);
223 dad-> setMinimumSize ((FIELDWIDTH*cell/2), (FIELDHEIGHT-3)*cell);
224
225 // Avoid spilling into the Taskbar or Apple Dock area if they get too close.
226 // Otherwise allow the dialog to choose its size and then be resizeable.
227 const QRect avail = QApplication::desktop()->availableGeometry(this);
228 if ((avail.height() - slParent->height()) <= 120) {
229 dad->setFixedHeight (slParent->height() - 120); // Keep 120 for buttons.
230 }
231
232 // Set the default for the level-number in the scrollbar.
233 number-> setTracking (true);
234 number->setValue (defaultLevel);
235
236 slSetGames (defaultGame);
237
238 // Vary the dialog according to the action.
239 QString OKText;
240 switch (slAction) {
241 case SL_START: // Must start at level 1, but can choose a game.
242 OKText = i18n ("Start Game");
243 number->setValue (1);
244 number->setEnabled (false);
245 display->setEnabled (false);
246 number->hide();
247 numberL->hide();
248 display->hide();
249 break;
250 case SL_ANY: // Can start playing at any level in any game.
251 OKText = i18n ("Play Level");
252 break;
253 case SL_REPLAY: // Can ask to see a replay of any level in any game.
254 OKText = i18n ("Replay Level");
255 break;
256 case SL_SOLVE: // Can ask to see a solution of any level in any game.
257 OKText = i18n ("Show Solution");
258 break;
259 case SL_SAVE_SOLUTION: // Can ask to save a recording on a solution-file.
260 OKText = i18n ("Save A Solution");
261 break;
262 case SL_UPDATE: // Can use any level in any game as edit input.
263 OKText = i18n ("Edit Level");
264 break;
265 case SL_CREATE: // Can save a new level only in a USER game.
266 OKText = i18n ("Save New");
267 break;
268 case SL_SAVE: // Can save an edited level only in a USER game.
269 OKText = i18n ("Save Change");
270 break;
271 case SL_DELETE: // Can delete a level only in a USER game.
272 OKText = i18n ("Delete Level");
273 break;
274 case SL_MOVE: // Can move a level only into a USER game.
275 OKText = i18n ("Move To...");
276 break;
277 case SL_UPD_GAME: // Can only edit USER game details.
278 OKText = i18n ("Edit Game Info");
279 number->setValue (1);
280 number->setEnabled (false);
281 display->setEnabled (false);
282 number->hide();
283 numberL->hide();
284 display->hide();
285 break;
286
287 default: break; // Keep the default settings.
288 }
289 if (!OKText.isEmpty()) {
290 KGuiItem::assign(levelNH, KGuiItem (OKText));
291 }
292
293 // Set value in the line-edit box.
294 slShowLevel (number->value());
295
296 if (display->isEnabled()) {
297 display->setFocus(); // Set the keyboard input on.
298 display->selectAll();
299 }
300
301 // Paint a thumbnail sketch of the level.
302 thumbNail->setFrameStyle (QFrame::Box | QFrame::Plain);
303 thumbNail->setLineWidth (1);
304 slPaintLevel();
305 thumbNail->show();
306
307 connect(games, &QTreeWidget::itemSelectionChanged, this, &KGrSLDialog::slGame);
308
309 connect(display, &QSpinBox::textChanged, this, &KGrSLDialog::slUpdate);
310 connect(number, &QScrollBar::valueChanged, this, &KGrSLDialog::slShowLevel);
311
312 // Only enable name and hint dialog here if saving a new or edited level.
313 // At other times the name and hint have not been loaded or initialised yet.
314 if ((slAction == SL_CREATE) || (slAction == SL_SAVE)) {
315 // Signal editNameAndHint() relays the click to a KGrEditor connection.
316 connect(levelNH, &QPushButton::clicked, this, &KGrSLDialog::editNameAndHint);
317 }
318 else {
319 levelNH->setEnabled (false);
320 levelNH->hide();
321 }
322
323 connect(games, &QTreeWidget::itemSelectionChanged, this, &KGrSLDialog::slPaintLevel);
324 connect(number, &QScrollBar::sliderReleased, this, &KGrSLDialog::slPaintLevel);
325
326 connect(buttonBox->button(QDialogButtonBox::Help), &QPushButton::clicked, this, &KGrSLDialog::slotHelp);
327 }
328
329 /******************************************************************************/
330 /***************** LOAD THE LIST OF GAMES (COLLECTIONS) *****************/
331 /******************************************************************************/
332
slSetGames(int cIndex)333 void KGrSLDialog::slSetGames (int cIndex)
334 {
335 int i;
336 int imax = myGameList.count();
337
338 // Set values in the table that holds details of available games.
339 // The table is displayed in order of skill then the kind of rules.
340 games->clear();
341 slGameIndex = -1;
342
343 QList<char> sortOrder1, sortOrder2; // Crude, but effective.
344 sortOrder1 << 'N' << 'C' << 'T';
345 sortOrder2 << 'T' << 'K';
346
347 for (char sortItem1 : std::as_const(sortOrder1)) {
348 for (char sortItem2 : std::as_const(sortOrder2)) {
349 for (i = 0; i < imax; ++i) {
350 if ((myGameList.at (i)->skill == sortItem1) &&
351 (myGameList.at (i)->rules == sortItem2)) {
352 QStringList data;
353 data
354 << myGameList.at (i)->name
355 << ((myGameList.at (i)->rules == 'K') ?
356 i18nc ("Rules", "KGoldrunner") :
357 i18nc ("Rules", "Traditional"))
358 << QString().setNum (myGameList.at (i)->nLevels)
359 << ((myGameList.at (i)->skill == 'T') ?
360 i18nc ("Skill Level", "Tutorial") :
361 ((myGameList.at (i)->skill == 'N') ?
362 i18nc ("Skill Level", "Normal") :
363 i18nc ("Skill Level", "Championship")));
364 KGrGameListItem * thisGame = new KGrGameListItem (data, i);
365 games->addTopLevelItem (thisGame);
366
367 if (slGameIndex < 0) {
368 slGameIndex = i; // There is at least one game.
369 }
370 if (i == cIndex) {
371 // Mark the currently selected game (default 0).
372 games->setCurrentItem (thisGame);
373 }
374 }
375 } // End "for" loop.
376 }
377 }
378
379 if (slGameIndex < 0) {
380 return; // The game-list is empty (unlikely).
381 }
382
383 // Fetch and display information on the selected game.
384 slGame();
385
386 // Make the column for the game's name a bit wider.
387 games->header()->setSectionResizeMode (0, QHeaderView::ResizeToContents);
388 games->header()->setSectionResizeMode (1, QHeaderView::ResizeToContents);
389 games->header()->setSectionResizeMode (2, QHeaderView::ResizeToContents);
390 }
391
392 /******************************************************************************/
393 /***************** SLOTS USED BY LEVEL SELECTION DIALOG *****************/
394 /******************************************************************************/
395
slGame()396 void KGrSLDialog::slGame()
397 {
398
399 if (slGameIndex < 0) {
400 // Ignore the "highlighted" signal caused by inserting in an empty box.
401 return;
402 }
403
404 if (games->selectedItems().size() <= 0) {
405 return;
406 }
407
408 slGameIndex = (dynamic_cast<KGrGameListItem *>
409 (games->selectedItems().first()))->id();
410 int n = slGameIndex; // Game selected.
411 int N = defaultGame; // Current game.
412 if (myGameList.at (n)->nLevels > 0) {
413 number->setMaximum (myGameList.at (n)->nLevels);
414 display->setMaximum (myGameList.at (n)->nLevels);
415 }
416 else {
417 number->setMaximum (1); // Avoid range errors.
418 display->setMaximum (1);
419 }
420
421 KConfigGroup gameGroup (KSharedConfig::openConfig(), "KDEGame");
422 int lev = 1;
423
424 // Set a default level number for the selected game.
425 switch (slAction) {
426 case SL_ANY:
427 case SL_REPLAY:
428 case SL_SOLVE:
429 case SL_SAVE_SOLUTION:
430 case SL_UPDATE:
431 case SL_DELETE:
432 case SL_UPD_GAME:
433 // If selecting the current game, use the current level number.
434 if (n == N) {
435 number->setValue (defaultLevel);
436 }
437 // Else use the last level played in the selected game (from KConfig).
438 else {
439 lev = gameGroup.readEntry (QLatin1String("Level_") + myGameList.at (n)->prefix, 1);
440 number->setValue (lev); // Else use level 1.
441 }
442 break;
443 case SL_CREATE:
444 case SL_SAVE:
445 case SL_MOVE:
446 if ((n == N) && (slAction != SL_CREATE)) {
447 // Saving/moving level in current game: use current number.
448 number->setValue (defaultLevel);
449 }
450 else {
451 // Saving new/edited level or relocating a level: use "nLevels + 1".
452 number->setMaximum (myGameList.at (n)->nLevels + 1);
453 display->setMaximum (myGameList.at (n)->nLevels + 1);
454 number->setValue (number->maximum());
455 }
456 break;
457 default:
458 number->setValue (1); // Default is level 1.
459 break;
460 }
461
462 slShowLevel (number->value());
463
464 int levCnt = myGameList.at (n)->nLevels;
465 if (myGameList.at (n)->rules == 'K')
466 gameD->setText (i18np ("1 level, uses KGoldrunner rules.",
467 "%1 levels, uses KGoldrunner rules.", levCnt));
468 else
469 gameD->setText (i18np ("1 level, uses Traditional rules.",
470 "%1 levels, uses Traditional rules.", levCnt));
471 gameN->setText (myGameList.at (n)->name);
472 QString s;
473 if (myGameList.at (n)->about.isEmpty()) {
474 s = i18n ("Sorry, there is no further information about this game.");
475 }
476 else {
477 s = (i18n (myGameList.at (n)->about.constData()));
478 }
479 gameAbout->setText (s);
480 }
481
slShowLevel(int i)482 void KGrSLDialog::slShowLevel (int i)
483 {
484 // Display the level number as the slider is moved.
485 display->setValue (i);
486 }
487
slUpdate(const QString & text)488 void KGrSLDialog::slUpdate (const QString & text)
489 {
490 // Move the slider when a valid level number is entered.
491 QString s = text;
492 bool ok = false;
493 int n = s.toInt (&ok);
494 if (ok) {
495 number->setValue (n);
496 slPaintLevel();
497 }
498 else
499 KGrMessage::information (this, i18n ("Select Level"),
500 i18n ("This level number is not valid. It can not be used."));
501 }
502
slPaintLevel()503 void KGrSLDialog::slPaintLevel()
504 {
505 // Repaint the thumbnail sketch of the level whenever the level changes.
506 if (slGameIndex < 0) {
507 return; // Owner has no games.
508 }
509 // Fetch level-data and save layout, name and label in the thumbnail.
510 QString dir = (myGameList.at (slGameIndex)->owner == USER) ?
511 userDir : systemDir;
512 thumbNail->setLevelData (dir, myGameList.at (slGameIndex)->prefix,
513 number->value(), slName);
514 thumbNail->repaint(); // Will call "paintEvent (e)".
515 }
516
slotHelp()517 void KGrSLDialog::slotHelp()
518 {
519 // Help for "Select Game and Level" dialog box.
520 QString s =
521 i18n ("The main button at the bottom echoes the "
522 "menu action you selected. Click it after choosing "
523 "a game and level - or use \"Cancel\".");
524
525 if (slAction == SL_START) {
526 s += i18n ("\n\nIf this is your first time in KGoldrunner, select the "
527 "tutorial game, which gives you hints as you go.\n\n"
528 "Otherwise, just click on the name of a game in the table, "
529 "then, to start at level 001, click on the main button at the "
530 "bottom. Play begins when you move the mouse or press a key.");
531 }
532 else {
533 switch (slAction) {
534 case SL_UPDATE:
535 s += i18n ("\n\nYou can select System levels for editing (or "
536 "copying), but you must save the result in a game you have "
537 "created. Use the left mouse-button as a paintbrush and the "
538 "editor toolbar buttons as a palette. Use the 'Erase' button "
539 "or the right mouse-button to erase. You can drag the mouse "
540 "with a button held down and paint or erase multiple squares.");
541 break;
542 case SL_CREATE:
543 s += i18n("\n\nYou can add a name and hint to your new level here, "
544 "but you must save the level you have created into one of "
545 "your own games. By default your new level will go at the "
546 "end of your game, but you can also select a level number and "
547 "save into the middle of your game.");
548 break;
549 case SL_SAVE:
550 s += i18n("\n\nYou can create or edit a name and hint here, before "
551 "saving. If you change the game or level, you can do a copy "
552 "or \"Save As\", but you must always save into one of your "
553 "own games. If you save a level into the middle of a series, "
554 "the other levels are automatically re-numbered.");
555 break;
556 case SL_DELETE:
557 s += i18n ("\n\nYou can only delete levels from one of your own "
558 "games. If you delete a level from the middle of a series, "
559 "the other levels are automatically re-numbered.");
560 break;
561 case SL_MOVE:
562 s += i18n ("\n\nTo move (re-number) a level, you must first select "
563 "it by using \"Edit Any Level...\", then you can use "
564 "\"Move Level...\" to move it to a new number or even a "
565 "different game. Other levels are automatically re-numbered as "
566 "required. You can only move levels within your own games.");
567 break;
568 case SL_UPD_GAME:
569 s += i18n ("\n\nWhen editing game info you only need to choose a "
570 "game, then you can go to a dialog where you edit the "
571 "details of the game.");
572 break;
573 default:
574 break;
575 }
576 s += i18n ("\n\nClick on the table to choose a game. "
577 "In the table and below it you can see more information about the "
578 "selected game, including how many levels there are, how difficult "
579 "the game is and what "
580 "rules the enemies follow (see the KGoldrunner Handbook).\n\n"
581 "You select "
582 "a level number by typing it or using the spin box or scroll bar. "
583 "As you vary the game or level, the thumbnail area shows a "
584 "preview of your choice.");
585 }
586
587 KGrMessage::information (slParent, i18n ("Help: Select Game & Level"), s);
588 }
589
590 /*******************************************************************************
591 ************************* ITEM FOR THE LIST OF GAMES *************************
592 *******************************************************************************/
593
KGrGameListItem(const QStringList & data,const int internalId)594 KGrGameListItem::KGrGameListItem (const QStringList & data,
595 const int internalId)
596 : QTreeWidgetItem (data)
597 {
598 mInternalId = internalId;
599 }
600
id() const601 int KGrGameListItem::id() const
602 {
603 return mInternalId;
604 }
605
setId(const int internalId)606 void KGrGameListItem::setId (const int internalId)
607 {
608 mInternalId = internalId;
609 }
610
611 /******************************************************************************/
612 /********************** CLASS TO DISPLAY THUMBNAIL ***********************/
613 /******************************************************************************/
614
KGrThumbNail(QWidget * parent)615 KGrThumbNail::KGrThumbNail (QWidget * parent)
616 :
617 QFrame (parent),
618 io (new KGrGameIO (parent))
619 {
620 // Let the parent do all the work. We need a class here so that
621 // QFrame::paintEvent (QPaintEvent *) can be re-implemented and
622 // the thumbnail can be automatically re-painted when required.
623 }
624
~KGrThumbNail()625 KGrThumbNail::~KGrThumbNail()
626 {
627 delete io;
628 }
629
setLevelData(const QString & dir,const QString & prefix,int level,QLabel * sln)630 void KGrThumbNail::setLevelData (const QString & dir, const QString& prefix,
631 int level, QLabel * sln)
632 {
633 KGrLevelData d;
634 QString filePath;
635
636 IOStatus stat = io->fetchLevelData (dir, prefix, level, d, filePath);
637 if (stat == OK) {
638 // Keep a safe copy of the layout. Translate and display the name.
639 levelLayout = d.layout;
640 sln->setText ((d.name.size() > 0) ? i18n (d.name.constData()) : QString());
641 }
642 else {
643 // Level-data inaccessible or not found.
644 levelLayout = "";
645 sln->setText (QString());
646 }
647 }
648
paintEvent(QPaintEvent *)649 void KGrThumbNail::paintEvent (QPaintEvent * /* event (unused) */)
650 {
651 QPainter p (this);
652 QFile openFile;
653 QPen pen = p.pen();
654 char obj = FREE;
655 int fw = 1; // Set frame width.
656 int n = width() / FIELDWIDTH; // Set thumbnail cell-size.
657
658 QColor backgroundColor = QColor ("#000038"); // Midnight blue.
659 QColor brickColor = QColor ("#9c0f0f"); // Oxygen's brick-red.
660 QColor concreteColor = QColor ("#585858"); // Dark grey.
661 QColor ladderColor = QColor ("#a0a0a0"); // Steely grey.
662 QColor poleColor = QColor ("#a0a0a0"); // Steely grey.
663 QColor heroColor = QColor ("#00ff00"); // Green.
664 QColor enemyColor = QColor ("#0080ff"); // Bright blue.
665 QColor gold;
666 gold.setNamedColor (QStringLiteral("gold")); // Gold.
667
668 pen.setColor (backgroundColor);
669 p.setPen (pen);
670
671 if (levelLayout.isEmpty()) {
672 // There is no file, so fill the thumbnail with "FREE" cells.
673 p.drawRect (QRect (fw, fw, FIELDWIDTH*n, FIELDHEIGHT*n));
674 return;
675 }
676
677 for (int j = 0; j < FIELDHEIGHT; j++)
678 for (int i = 0; i < FIELDWIDTH; i++) {
679
680 obj = levelLayout.at (j*FIELDWIDTH + i);
681
682 // Set the colour of each object.
683 switch (obj) {
684 case BRICK:
685 case FBRICK:
686 pen.setColor (brickColor); p.setPen (pen); break;
687 case CONCRETE:
688 pen.setColor (concreteColor); p.setPen (pen); break;
689 case LADDER:
690 pen.setColor (ladderColor); p.setPen (pen); break;
691 case BAR:
692 pen.setColor (poleColor); p.setPen (pen); break;
693 case HERO:
694 pen.setColor (heroColor); p.setPen (pen); break;
695 case ENEMY:
696 pen.setColor (enemyColor); p.setPen (pen); break;
697 default:
698 // Set the background colour for FREE, HLADDER and NUGGET.
699 pen.setColor (backgroundColor); p.setPen (pen); break;
700 }
701
702 // Draw n x n pixels as n lines of length n.
703 p.drawLine (i*n+fw, j*n+fw, i*n+(n-1)+fw, j*n+fw);
704 if (obj == BAR) {
705 // For a pole, only the top line is drawn in white.
706 pen.setColor (backgroundColor);
707 p.setPen (pen);
708 }
709 for (int k = 1; k < n; k++) {
710 p.drawLine (i*n+fw, j*n+k+fw, i*n+(n-1)+fw, j*n+k+fw);
711 }
712
713 // For a nugget, add just a vertical touch of yellow (2-3 pixels).
714 if (obj == NUGGET) {
715 int k = (n/2)+fw;
716 pen.setColor (gold); // Gold.
717 p.setPen (pen);
718 p.drawLine (i*n+k, j*n+k, i*n+k, j*n+(n-1)+fw);
719 p.drawLine (i*n+k+1, j*n+k, i*n+k+1, j*n+(n-1)+fw);
720 }
721 }
722
723 // Finally, draw a small black border around the outside of the thumbnail.
724 pen.setColor (Qt::black);
725 p.setPen (pen);
726 p.drawRect (rect().left(), rect().top(), rect().right(), rect().bottom());
727 }
728
729
730