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