File indexing completed on 2022-08-04 15:10:16

0001 /*
0002  *  ksokoban - a Sokoban game by KDE
0003  *  Copyright (C) 1998  Anders Widell  <d95-awi@nada.kth.se>
0004  *
0005  *  This program is free software; you can redistribute it and/or modify
0006  *  it under the terms of the GNU General Public License as published by
0007  *  the Free Software Foundation; either version 2 of the License, or
0008  *  (at your option) any later version.
0009  *
0010  *  This program is distributed in the hope that it will be useful,
0011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  *  GNU General Public License for more details.
0014  *
0015  *  You should have received a copy of the GNU General Public License
0016  *  along with this program; if not, write to the Free Software
0017  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0018  */
0019 
0020 #include <stdio.h>
0021 #include <assert.h>
0022 
0023 #include <QWidget>
0024 #include <QPixmap>
0025 #include <QKeyEvent>
0026 #include <QApplication>
0027 #include <KSharedConfig>
0028 #include <KMessageBox>
0029 #include <KConfigGroup>
0030 #include <KLocalizedString>
0031 #include <QPainter>
0032 #include <QMouseEvent>
0033 #include <QWheelEvent>
0034 #include <QFontDatabase>
0035 
0036 #include "PlayField.h"
0037 #include "ModalLabel.h"
0038 #include "LevelMap.h"
0039 #include "Move.h"
0040 #include "History.h"
0041 #include "PathFinder.h"
0042 #include "MapDelta.h"
0043 #include "MoveSequence.h"
0044 #include "StaticImage.h"
0045 #include "HtmlPrinter.h"
0046 #include "Bookmark.h"
0047 #include "LevelCollection.h"
0048 
0049 PlayField::PlayField(QWidget *parent)
0050   : QWidget(parent), imageData_(nullptr), lastLevel_(-1),
0051     moveSequence_(nullptr), moveInProgress_(false), dragInProgress_(false),
0052     xOffs_(0), yOffs_(0),
0053     wheelDelta_(0), debug_counter(0),
0054     sizeAllCursor(Qt::SizeAllCursor), crossCursor(Qt::CrossCursor),
0055     levelText_(i18n("Level:")), stepsText_(i18n("Steps:")),
0056     pushesText_(i18n("Pushes:")),
0057     pnumXpm_(nullptr), ptxtXpm_(nullptr), snumXpm_(nullptr), stxtXpm_(nullptr),
0058     lnumXpm_(nullptr), ltxtXpm_(nullptr), collXpm_(nullptr),
0059     statusFont_(QFontDatabase::systemFont(QFontDatabase::GeneralFont).family(), 18, QFont::Bold),
0060     statusMetrics_(statusFont_) {
0061 
0062   //setAttribute(Qt::WA_PaintOutsidePaintEvent);
0063   setFocusPolicy(Qt::StrongFocus);
0064   setFocus();
0065   setMouseTracking(true);
0066 
0067   highlightX_ = highlightY_ = 0;
0068 
0069   KSharedConfigPtr cfg = KSharedConfig::openConfig();
0070   KConfigGroup settingsGroup(cfg, "settings");
0071   
0072   imageData_ = new StaticImage;
0073 
0074   animDelay_ = settingsGroup.readEntry("animDelay", QStringLiteral("2")).toInt();
0075   if (animDelay_ < 0 || animDelay_ > 3) animDelay_ = 2;
0076 
0077   history_ = new History;
0078 
0079   background_.setTexture(imageData_->background());
0080   //floor_ = QColor(0x66,0x66,0x66, 255);
0081 
0082   levelMap_  = new LevelMap;
0083   mapDelta_ = new MapDelta(levelMap_);
0084   mapDelta_->end();
0085 
0086   levelChange();
0087 }
0088 
0089 PlayField::~PlayField() {
0090   KSharedConfigPtr cfg = KSharedConfig::openConfig();
0091   KConfigGroup settingsGroup(cfg, "settings");
0092   settingsGroup.writeEntry("animDelay", QStringLiteral("%1").arg(animDelay_));
0093 
0094   delete mapDelta_;
0095   delete history_;
0096   delete levelMap_;
0097   delete imageData_;
0098   if(ltxtXpm_) 
0099     delete ltxtXpm_;
0100   if(lnumXpm_)
0101     delete lnumXpm_;
0102   if(stxtXpm_)
0103     delete stxtXpm_;
0104   if(snumXpm_)
0105     delete snumXpm_;
0106   if(ptxtXpm_)
0107     delete ptxtXpm_;
0108   if(pnumXpm_)
0109     delete pnumXpm_;
0110   if(collXpm_)
0111     delete collXpm_;
0112 }
0113 
0114 void
0115 PlayField::changeCursor(const QCursor* c) {
0116   if (cursor_ == c) return;
0117 
0118   cursor_ = c;
0119   if (c == nullptr) unsetCursor();
0120   else setCursor(*c);
0121 }
0122 
0123 int
0124 PlayField::level() const {
0125   if (levelMap_ == nullptr) return 0;
0126   return levelMap_->level();
0127 }
0128 
0129 const QString &
0130 PlayField::collectionName() {
0131   static QString error = QStringLiteral("????");
0132   if (levelMap_ == nullptr) return error;
0133   return levelMap_->collectionName();
0134 }
0135 
0136 int
0137 PlayField::totalMoves() const {
0138   if (levelMap_ == nullptr) return 0;
0139   return levelMap_->totalMoves();
0140 }
0141 
0142 int
0143 PlayField::totalPushes() const{
0144   if (levelMap_ == nullptr) return 0;
0145   return levelMap_->totalPushes();
0146 }
0147 
0148 void
0149 PlayField::levelChange() {
0150   stopMoving();
0151   stopDrag();
0152   history_->clear();
0153   setSize(width(), height());
0154 
0155   updateLevelXpm();
0156   updateStepsXpm();
0157   updatePushesXpm();
0158   highlight();
0159 }
0160 
0161 void
0162 PlayField::paintSquare(int x, int y, QPainter &paint) {
0163   if (levelMap_->xpos() == x && levelMap_->ypos() == y) {
0164     if (levelMap_->goal(x, y))
0165       imageData_->saveman(paint, x2pixel(x), y2pixel(y));
0166     else {
0167       imageData_->man(paint, x2pixel(x), y2pixel(y));
0168       //printf("imageData_->man() %d; %d\n",x2pixel(x), y2pixel(y));
0169     } 
0170     return;
0171   }
0172   if (levelMap_->empty(x, y)) {
0173     if (levelMap_->floor(x, y)) {
0174       if (levelMap_->goal(x, y))
0175         imageData_->goal(paint, x2pixel(x), y2pixel(y));
0176       else{
0177         //paint.fillRect(x2pixel(x), y2pixel(y), size_, size_, floor_);
0178         imageData_->floor(paint, x2pixel(x), y2pixel(y));
0179         //printf("executing paint.fillRect(): %d; %d,%d,%d,%d\n",++debug_counter, x2pixel(x), y2pixel(y), size_, size_);
0180       }
0181     } else {
0182       paint.fillRect(x2pixel(x), y2pixel(y), size_, size_, background_);
0183     }
0184     return;
0185   }
0186   if (levelMap_->wall(x, y)) {
0187     imageData_->wall(paint, x2pixel(x), y2pixel(y), x+y*(MAX_X+1),
0188              levelMap_->wallLeft(x, y),
0189              levelMap_->wallRight(x, y));
0190     return;
0191   }
0192 
0193 
0194   if (levelMap_->object(x, y)) {
0195     if (highlightX_ == x && highlightY_ == y) {
0196       if (levelMap_->goal(x, y))
0197     imageData_->brightTreasure(paint, x2pixel(x), y2pixel(y));
0198       else
0199     imageData_->brightObject(paint, x2pixel(x), y2pixel(y));
0200     } else {
0201       if (levelMap_->goal(x, y))
0202     imageData_->treasure(paint, x2pixel(x), y2pixel(y));
0203       else
0204     imageData_->object(paint, x2pixel(x), y2pixel(y));
0205     }
0206     return;
0207   }
0208 }
0209 
0210 void
0211 PlayField::paintDelta() {
0212   repaint();
0213 }
0214 
0215 
0216 
0217 void
0218 PlayField::paintEvent(QPaintEvent *e) {
0219   QPainter paint;
0220   paint.begin(this);
0221   // the following line is a workaround for a bug in Qt 2.0.1
0222   // (and possibly earlier versions)
0223   paint.setBrushOrigin(0, 0);
0224 
0225   paint.setClipRegion(e->region());
0226   paint.setClipping(true);
0227 
0228   paintPainter(paint, e->rect());
0229   paint.end();
0230 }
0231 
0232 void
0233 PlayField::paintPainterClip(QPainter &paint, int x, int y, int w, int h) {
0234   QRect rect(x, y, w, h);
0235 
0236   paint.setClipRect(rect);
0237   paint.setClipping(true);
0238   paintPainter(paint, rect);
0239 }
0240 
0241 void
0242 PlayField::paintPainter(QPainter &paint, const QRect &rect) {
0243   if (size_ <= 0) return;
0244   int minx = pixel2x(rect.x());
0245   int miny = pixel2y(rect.y());
0246   int maxx = pixel2x(rect.x()+rect.width()-1);
0247   int maxy = pixel2y(rect.y()+rect.height()-1);
0248 
0249   if (minx < 0) minx = 0;
0250   if (miny < 0) miny = 0;
0251   if (maxx >= levelMap_->width()) maxx = levelMap_->width()-1;
0252   if (maxy >= levelMap_->height()) maxy = levelMap_->height()-1;
0253 
0254   {
0255     int x1, x2, y1, y2;
0256     y1 = y2pixel(miny);
0257     if (y1 > rect.y()) paint.fillRect(rect.x(), rect.y(), rect.width(), y1-rect.y(), background_);
0258 
0259     int bot=rect.y()+rect.height();
0260     if (bot > height()-collRect_.height()) bot = height()-collRect_.height();
0261 
0262     y2 = y2pixel(maxy+1);
0263     if (y2 < bot) paint.fillRect(rect.x(), y2, rect.width(), bot-y2, background_);
0264 
0265     x1 = x2pixel(minx);
0266     if (x1 > rect.x()) paint.fillRect(rect.x(), y1, x1-rect.x(), y2-y1, background_);
0267 
0268     x2 = x2pixel(maxx+1);
0269     if (x2 < rect.x()+rect.width()) paint.fillRect(x2, y1, rect.x()+rect.width()-x2, y2-y1, background_);
0270 
0271     // paint.eraseRect
0272   }
0273 
0274   for (int y=miny; y<=maxy; y++) {
0275     for (int x=minx; x<=maxx; x++) {
0276       paintSquare(x, y, paint);
0277     }
0278   }
0279 
0280   if (collRect_.intersects(rect) && collXpm_)  paint.drawPixmap(collRect_.x(), collRect_.y(), *collXpm_);
0281   if (ltxtRect_.intersects(rect) && ltxtXpm_)  paint.drawPixmap(ltxtRect_.x(), ltxtRect_.y(), *ltxtXpm_);
0282   if (lnumRect_.intersects(rect) && lnumXpm_)  paint.drawPixmap(lnumRect_.x(), lnumRect_.y(), *lnumXpm_);
0283   if (stxtRect_.intersects(rect) && stxtXpm_)  paint.drawPixmap(stxtRect_.x(), stxtRect_.y(), *stxtXpm_);
0284   if (snumRect_.intersects(rect) && snumXpm_)  paint.drawPixmap(snumRect_.x(), snumRect_.y(), *snumXpm_);
0285   if (ptxtRect_.intersects(rect) && ptxtXpm_)  paint.drawPixmap(ptxtRect_.x(), ptxtRect_.y(), *ptxtXpm_);
0286   if (pnumRect_.intersects(rect) && pnumXpm_)  paint.drawPixmap(pnumRect_.x(), pnumRect_.y(), *pnumXpm_);
0287 }
0288 
0289 void
0290 PlayField::resizeEvent(QResizeEvent *e) {
0291   setSize(e->size().width(), e->size().height());
0292 }
0293 
0294 void
0295 PlayField::mouseMoveEvent(QMouseEvent *e) {
0296   lastMouseXPos_ = e->x();
0297   lastMouseYPos_ = e->y();
0298 
0299   if (!dragInProgress_) return highlight();
0300 
0301   int old_x = dragX_, old_y = dragY_;
0302 
0303   dragX_ = lastMouseXPos_ - mousePosX_;
0304   dragY_ = lastMouseYPos_ - mousePosY_;
0305 
0306   {
0307     int x = pixel2x(dragX_ + size_/2);
0308     int y = pixel2y(dragY_ + size_/2);
0309     if (x >= 0 && x < levelMap_->width() &&
0310     y >= 0 && y < levelMap_->height() &&
0311     pathFinder_.canDragTo(x, y)) {
0312       x = x2pixel(x);
0313       y = y2pixel(y);
0314 
0315       if (dragX_ >= x - size_/4 &&
0316       dragX_ <  x + size_/4 &&
0317       dragY_ >= y - size_/4 &&
0318       dragY_ <  y + size_/4) {
0319     dragX_ = x;
0320     dragY_ = y;
0321       }
0322     }
0323   }
0324 
0325   if (dragX_ == old_x && dragY_ == old_y) return;
0326 
0327   repaint();
0328   
0329 }
0330 
0331 void
0332 PlayField::highlight() {
0333   // FIXME: the line below should not be needed
0334   if (size_ == 0) return;
0335 
0336   int x=pixel2x(lastMouseXPos_);
0337   int y=pixel2y(lastMouseYPos_);
0338 
0339   if (x < 0 || y < 0 || x >= levelMap_->width() || y >= levelMap_->height())
0340     return;
0341 
0342   if (x == highlightX_ && y == highlightY_) return;
0343 
0344   if (pathFinder_.canDrag(x, y)) {
0345     highlightX_ = x;
0346     highlightY_ = y;
0347     repaint();
0348   } else {
0349     if (pathFinder_.canWalkTo(x, y)) changeCursor(&crossCursor);
0350     else changeCursor(nullptr);
0351     if (highlightX_ >= 0) {
0352       repaint();
0353     }
0354   }
0355 }
0356 
0357 void
0358 PlayField::stopMoving() {
0359   killTimers();
0360   delete moveSequence_;
0361   moveSequence_ = nullptr;
0362   moveInProgress_ = false;
0363   updateStepsXpm();
0364   updatePushesXpm();
0365 
0366   repaint();
0367   pathFinder_.updatePossibleMoves();
0368 }
0369 
0370 
0371 void
0372 PlayField::startMoving(Move *m) {
0373   startMoving(new MoveSequence(m, levelMap_));
0374 }
0375 
0376 void
0377 PlayField::startMoving(MoveSequence *ms) {
0378   static const int delay[4] = {0, 15, 35, 60};
0379 
0380   assert(moveSequence_ == nullptr && !moveInProgress_);
0381   moveSequence_ = ms;
0382   moveInProgress_ = true;
0383   if (animDelay_) timers.append(startTimer(delay[animDelay_]));
0384   timerEvent(nullptr);
0385 }
0386 
0387 void
0388 PlayField::timerEvent(QTimerEvent *) {
0389   assert(moveInProgress_);
0390   if (moveSequence_ == nullptr) {
0391     killTimers();
0392     moveInProgress_ = false;
0393     return;
0394   }
0395 
0396   bool more=false;
0397 
0398   mapDelta_->start();
0399   if (animDelay_) more = moveSequence_->next();
0400   else {
0401     while (moveSequence_->next()) if (levelMap_->completed()) break;
0402     more = true;   // FIXME: clean this up
0403     stopMoving();
0404   }
0405   mapDelta_->end();
0406 
0407   if (more) {
0408     paintDelta();
0409     if (levelMap_->completed()) {
0410       stopMoving();
0411       ModalLabel::message(i18n("Level completed"), this);
0412       nextLevel();
0413       return;
0414     }
0415   } else stopMoving();
0416 }
0417 
0418 void
0419 PlayField::step(int _x, int _y) {
0420   if (!canMoveNow()) return;
0421 
0422   int oldX=levelMap_->xpos();
0423   int oldY=levelMap_->ypos();
0424   int x=oldX, y=oldY;
0425 
0426   int dx=0, dy=0;
0427   if (_x>oldX) dx=1;
0428   if (_x<oldX) dx=-1;
0429   if (_y>oldY) dy=1;
0430   if (_y<oldY) dy=-1;
0431 
0432   while (!(x==_x && y==_y) && levelMap_->step(x+dx, y+dy)) {
0433     x += dx;
0434     y += dy;
0435   }
0436 
0437   if (x!=oldX || y!=oldY) {
0438     Move *m = new Move(oldX, oldY);
0439     m->step(x, y);
0440     m->finish();
0441     history_->add(m);
0442     m->undo(levelMap_);
0443 
0444     startMoving(m);
0445 
0446   }
0447 }
0448 
0449 void
0450 PlayField::push(int _x, int _y) {
0451   if (!canMoveNow()) return;
0452 
0453   int oldX=levelMap_->xpos();
0454   int oldY=levelMap_->ypos();
0455   int x=oldX, y=oldY;
0456 
0457   int dx=0, dy=0;
0458   if (_x>oldX) dx=1;
0459   if (_x<oldX) dx=-1;
0460   if (_y>oldY) dy=1;
0461   if (_y<oldY) dy=-1;
0462 
0463   while (!(x==_x && y==_y) && levelMap_->step(x+dx, y+dy)) {
0464     x += dx;
0465     y += dy;
0466   }
0467   int objX=x, objY=y;
0468   while (!(x==_x && y==_y) && levelMap_->push(x+dx, y+dy)) {
0469     x += dx;
0470     y += dy;
0471   }
0472 
0473   if (x!=oldX || y!=oldY) {
0474     Move *m = new Move(oldX, oldY);
0475 
0476     if (objX!=oldX || objY!=oldY) m->step(objX, objY);
0477 
0478     if (objX!=x || objY!=y) {
0479       m->push(x, y);
0480 
0481       objX += dx;
0482       objY += dy;
0483     }
0484     m->finish();
0485     history_->add(m);
0486 
0487     m->undo(levelMap_);
0488 
0489     startMoving(m);
0490   }
0491 }
0492 
0493 void
0494 PlayField::keyPressEvent(QKeyEvent * e) {
0495   int x=levelMap_->xpos();
0496   int y=levelMap_->ypos();
0497 
0498   switch (e->key()) {
0499   case Qt::Key_Up:
0500     if (e->modifiers() & Qt::ControlModifier) step(x, 0);
0501     else if (e->modifiers() & Qt::ShiftModifier) push(x, 0);
0502     else push(x, y-1);
0503     break;
0504   case Qt::Key_Down:
0505     if (e->modifiers() & Qt::ControlModifier) step(x, MAX_Y);
0506     else if (e->modifiers() & Qt::ShiftModifier) push(x, MAX_Y);
0507     else push(x, y+1);
0508     break;
0509   case Qt::Key_Left:
0510     if (e->modifiers() & Qt::ControlModifier) step(0, y);
0511     else if (e->modifiers() & Qt::ShiftModifier) push(0, y);
0512     else push(x-1, y);
0513     break;
0514   case Qt::Key_Right:
0515     if (e->modifiers() & Qt::ControlModifier) step(MAX_X, y);
0516     else if (e->modifiers() & Qt::ShiftModifier) push(MAX_X, y);
0517     else push(x+1, y);
0518     break;
0519 
0520   case Qt::Key_Q:
0521     qApp->closeAllWindows();
0522     break;
0523 
0524   case Qt::Key_Backspace:
0525   case Qt::Key_Delete:
0526     if (e->modifiers() & Qt::ControlModifier) redo();
0527     else undo();
0528     break;
0529 
0530 #if 0
0531   case Qt::Key_X:
0532     levelMap_->random();
0533     levelChange();
0534     repaint(false);
0535     break;
0536 
0537   case Qt::Key_R:
0538     level(levelMap_->level());
0539     return;
0540     break;
0541   case Qt::Key_N:
0542     nextLevel();
0543     return;
0544     break;
0545   case Qt::Key_P:
0546     previousLevel();
0547     return;
0548     break;
0549   case Qt::Key_U:
0550     undo();
0551     return;
0552     break;
0553   case Qt::Key_I:
0554     history_->redo(levelMap_);
0555     repaint(false);
0556     return;
0557     break;
0558 
0559   case Qt::Key_S:
0560     {
0561       QString buf;
0562       history_->save(buf);
0563       printf("%s\n", (char *) buf);
0564     }
0565     return;
0566     break;
0567 
0568   case Qt::Key_L:
0569     stopMoving();
0570     history_->clear();
0571     level(levelMap_->level());
0572     {
0573       char buf[4096]="r1*D1*D1*r1*@r1*D1*";
0574       //scanf("%s", buf);
0575       history_->load(levelMap_, buf);
0576     }
0577     updateStepsXpm();
0578     updatePushesXpm();
0579     repaint(false);
0580     return;
0581     break;
0582 #endif
0583 
0584 
0585   case Qt::Key_Print:
0586     HtmlPrinter::printHtml(levelMap_);
0587     break;
0588 
0589   default:
0590     e->ignore();
0591     return;
0592   }
0593 }
0594 
0595 void
0596 PlayField::stopDrag() {
0597   if (!dragInProgress_) return;
0598 
0599   changeCursor(nullptr);
0600 
0601   repaint();
0602   dragInProgress_ = false;
0603 }
0604 
0605 void
0606 PlayField::dragObject(int xpixel, int ypixel) {
0607   int x=pixel2x(xpixel - mousePosX_ + size_/2);
0608   int y=pixel2y(ypixel - mousePosY_ + size_/2);
0609 
0610   if (x == highlightX_ && y == highlightY_) return;
0611 
0612   printf("drag %d,%d to %d,%d\n", highlightX_, highlightY_, x, y);
0613   pathFinder_.drag(highlightX_, highlightY_, x, y);
0614   stopDrag();
0615 }
0616 
0617 
0618 void
0619 PlayField::mousePressEvent(QMouseEvent *e) {
0620   if (!canMoveNow()) return;
0621 
0622   if (dragInProgress_) {
0623     if (e->button() == Qt::LeftButton) dragObject(e->x(), e->y());
0624     else stopDrag();
0625     return;
0626   }
0627 
0628   int x=pixel2x(e->x());
0629   int y=pixel2y(e->y());
0630 
0631   if (x < 0 || y < 0 || x >= levelMap_->width() || y >= levelMap_->height())
0632     return;
0633 
0634   if (e->button() == Qt::LeftButton && pathFinder_.canDrag(x, y)) {
0635     repaint();
0636     highlightX_ = x;
0637     highlightY_ = y;
0638     pathFinder_.updatePossibleDestinations(x, y);
0639 
0640     dragX_ = x2pixel(x);
0641     dragY_ = y2pixel(y);
0642     mousePosX_ = e->x() - dragX_;
0643     mousePosY_ = e->y() - dragY_;
0644     dragInProgress_ = true;
0645   }
0646 
0647   Move *m;
0648   switch (e->button()) {
0649   case Qt::LeftButton:
0650     m = pathFinder_.search(levelMap_, x, y);
0651     if (m != nullptr) {
0652       history_->add(m);
0653 
0654       startMoving(m);
0655     }
0656     break;
0657   case Qt::MiddleButton:
0658     undo();
0659     return;
0660     break;
0661   case Qt::RightButton:
0662     push(x, y);
0663     break;
0664 
0665   default:
0666     return;
0667   }
0668 }
0669 
0670 void
0671 PlayField::wheelEvent(QWheelEvent *e) {
0672   wheelDelta_ += e->angleDelta().y();
0673 
0674   if (wheelDelta_ >= 120) {
0675     wheelDelta_ %= 120;
0676     redo();
0677   } else if (wheelDelta_ <= -120) {
0678     wheelDelta_ = -(-wheelDelta_ % 120);
0679     undo();
0680   }
0681 }
0682 
0683 void
0684 PlayField::mouseReleaseEvent(QMouseEvent *e) {
0685   if (dragInProgress_) dragObject(e->x(), e->y());
0686 }
0687 
0688 
0689 void
0690 PlayField::focusInEvent(QFocusEvent *) {
0691   //printf("PlayField::focusInEvent\n");
0692 }
0693 
0694 void
0695 PlayField::focusOutEvent(QFocusEvent *) {
0696   //printf("PlayField::focusOutEvent\n");
0697 }
0698 
0699 void
0700 PlayField::leaveEvent(QEvent *) {
0701   stopDrag();
0702 }
0703 
0704 void
0705 PlayField::setSize(int w, int h) {
0706   int sbarHeight = statusMetrics_.height();
0707   int sbarNumWidth = statusMetrics_.boundingRect(QStringLiteral("88888")).width()+8;
0708   int sbarLevelWidth = statusMetrics_.boundingRect(levelText_).width()+8;
0709   int sbarStepsWidth = statusMetrics_.boundingRect(stepsText_).width()+8;
0710   int sbarPushesWidth = statusMetrics_.boundingRect(pushesText_).width()+8;
0711 
0712   pnumRect_.setRect(w-sbarNumWidth, h-sbarHeight, sbarNumWidth, sbarHeight);
0713   ptxtRect_.setRect(pnumRect_.x()-sbarPushesWidth, h-sbarHeight, sbarPushesWidth, sbarHeight);
0714   snumRect_.setRect(ptxtRect_.x()-sbarNumWidth, h-sbarHeight, sbarNumWidth, sbarHeight);
0715   stxtRect_.setRect(snumRect_.x()-sbarStepsWidth, h-sbarHeight, sbarStepsWidth, sbarHeight);
0716   lnumRect_.setRect(stxtRect_.x()-sbarNumWidth, h-sbarHeight, sbarNumWidth, sbarHeight);
0717   ltxtRect_.setRect(lnumRect_.x()-sbarLevelWidth, h-sbarHeight, sbarLevelWidth, sbarHeight);
0718   collRect_.setRect(0, h-sbarHeight, ltxtRect_.x(), sbarHeight);
0719 
0720   //printf("collRect_:%d;%d;%d;%d\n",collRect_.x(), collRect_.y(), collRect_.width(), collRect_.height());
0721   if(ltxtXpm_) 
0722     delete ltxtXpm_;
0723   if(lnumXpm_)
0724     delete lnumXpm_;
0725   if(stxtXpm_)
0726     delete stxtXpm_;
0727   if(snumXpm_)
0728     delete snumXpm_;
0729   if(ptxtXpm_)
0730     delete ptxtXpm_;
0731   if(pnumXpm_)
0732     delete pnumXpm_;
0733   if(collXpm_)
0734     delete collXpm_;
0735   ltxtXpm_ =  new QPixmap(ltxtRect_.size());    
0736   lnumXpm_ =  new QPixmap(lnumRect_.size());
0737   stxtXpm_ =  new QPixmap(stxtRect_.size());
0738   snumXpm_ =  new QPixmap(snumRect_.size());
0739   ptxtXpm_ =  new QPixmap(ptxtRect_.size());
0740   pnumXpm_ =  new QPixmap(pnumRect_.size());
0741   collXpm_ = new QPixmap(collRect_.size());
0742   
0743   h -= sbarHeight;
0744 
0745   int cols = levelMap_->width();
0746   int rows = levelMap_->height();
0747 
0748   // FIXME: the line below should not be needed
0749   if (cols == 0 || rows == 0) return;
0750 
0751   int xsize = w / cols;
0752   int ysize = h / rows;
0753 
0754   if (xsize < 8) xsize = 8;
0755   if (ysize < 8) ysize = 8;
0756 
0757   size_ = imageData_->resize(xsize > ysize ? ysize : xsize);
0758 
0759   xOffs_ = (w - cols*size_) / 2;
0760   yOffs_ = (h - rows*size_) / 2;
0761 
0762 
0763   updateCollectionXpm();
0764   updateTextXpm();
0765   updateLevelXpm();
0766   updateStepsXpm();
0767   updatePushesXpm();
0768 }
0769 
0770 void
0771 PlayField::nextLevel() {
0772   if (levelMap_->level()+1 >= levelMap_->noOfLevels()) {
0773     ModalLabel::message(i18n("\
0774 This is the last level in\n\
0775 the current collection."), this);
0776     return;
0777   }
0778   if (levelMap_->level() >= levelMap_->completedLevels()) {
0779     ModalLabel::message(i18n("\
0780 You have not completed\n\
0781 this level yet."), this);
0782     return;
0783   }
0784 
0785   level(levelMap_->level()+1);
0786   levelChange();
0787   repaint();
0788 }
0789 
0790 void
0791 PlayField::previousLevel() {
0792   if (levelMap_->level() <= 0) {
0793     ModalLabel::message(i18n("\
0794 This is the first level in\n\
0795 the current collection."), this);
0796     return;
0797   }
0798   level(levelMap_->level()-1);
0799   levelChange();
0800   repaint();
0801 }
0802 
0803 void
0804 PlayField::undo() {
0805   if (!canMoveNow()) return;
0806 
0807   startMoving(history_->deferUndo(levelMap_));
0808 }
0809 
0810 void
0811 PlayField::redo() {
0812   if (!canMoveNow()) return;
0813 
0814   startMoving(history_->deferRedo(levelMap_));
0815 }
0816 
0817 void
0818 PlayField::restartLevel() {
0819   stopMoving();
0820   history_->clear();
0821   level(levelMap_->level());
0822   updateStepsXpm();
0823   updatePushesXpm();
0824   repaint();
0825 }
0826 
0827 void
0828 PlayField::changeCollection(LevelCollection *collection) {
0829   if (levelMap_->collection() == collection) return;
0830   levelMap_->changeCollection(collection);
0831   levelChange();
0832   //erase(collRect_);
0833   repaint();
0834 }
0835 
0836 void
0837 PlayField::updateCollectionXpm() {
0838   if (!collXpm_) return;
0839   if (collXpm_->isNull()) return;
0840   //printf("executing PlayField::updateCollectionXpm() w:%d, h:%d\n",collXpm_->width(), collXpm_->height());
0841   
0842   QPainter paint(collXpm_);
0843   paint.setBrushOrigin(- collRect_.x(), - collRect_.y());
0844   paint.fillRect(0, 0, collRect_.width(), collRect_.height(), background_);
0845 
0846   paint.setFont(statusFont_);
0847   paint.setPen(QColor(0,255,0));
0848   paint.drawText(0, 0, collRect_.width(), collRect_.height(),
0849          Qt::AlignLeft, collectionName());
0850 }
0851 
0852 void
0853 PlayField::updateTextXpm() {
0854   if (!ltxtXpm_) return;
0855   if (ltxtXpm_->isNull()) return;
0856   //printf("executing PlayField::updateTextXpm() w:%d, h:%d\n",ltxtXpm_->width(), ltxtXpm_->height());
0857   
0858   QPainter paint;
0859 
0860   paint.begin(ltxtXpm_);
0861   paint.setBrushOrigin(- ltxtRect_.x(), - ltxtRect_.y());
0862   paint.fillRect(0, 0, ltxtRect_.width(), ltxtRect_.height(), background_);
0863   paint.setFont(statusFont_);
0864   paint.setPen(QColor(128,128,128));
0865   paint.drawText(0, 0, ltxtRect_.width(), ltxtRect_.height(), Qt::AlignLeft, levelText_);
0866   paint.end();
0867 
0868   paint.begin(stxtXpm_);
0869   paint.setBrushOrigin(- stxtRect_.x(), - stxtRect_.y());
0870   paint.fillRect(0, 0, stxtRect_.width(), stxtRect_.height(), background_);
0871   paint.setFont(statusFont_);
0872   paint.setPen(QColor(128,128,128));
0873   paint.drawText(0, 0, stxtRect_.width(), stxtRect_.height(), Qt::AlignLeft, stepsText_);
0874   paint.end();
0875 
0876   paint.begin(ptxtXpm_);
0877   paint.setBrushOrigin(- ptxtRect_.x(), - ptxtRect_.y());
0878   paint.fillRect(0, 0, ptxtRect_.width(), ptxtRect_.height(), background_);
0879   paint.setFont(statusFont_);
0880   paint.setPen(QColor(128,128,128));
0881   paint.drawText(0, 0, ptxtRect_.width(), ptxtRect_.height(), Qt::AlignLeft, pushesText_);
0882   paint.end();
0883 }
0884 
0885 void
0886 PlayField::updateLevelXpm() {
0887   if (!lnumXpm_) return;
0888   if (lnumXpm_->isNull()) return;
0889   //printf("executing PlayField::updateLevelXpm()\n");
0890   
0891   QPainter paint(lnumXpm_);
0892   paint.setBrushOrigin(- lnumRect_.x(), - lnumRect_.y());
0893   paint.fillRect(0, 0, lnumRect_.width(), lnumRect_.height(), background_);
0894 
0895   paint.setFont(statusFont_);
0896   paint.setPen(QColor(255,0,0));
0897   paint.drawText(0, 0, lnumRect_.width(), lnumRect_.height(),
0898                  Qt::AlignLeft, QString::asprintf("%05d", level()+1));
0899 }
0900 
0901 void
0902 PlayField::updateStepsXpm() {
0903   if (!snumXpm_) return;
0904   if (snumXpm_->isNull()) return;
0905   //printf("executing PlayField::updateStepsXpm()\n");
0906   
0907   QPainter paint(snumXpm_);
0908   paint.setBrushOrigin(- snumRect_.x(), - snumRect_.y());
0909   paint.fillRect(0, 0, snumRect_.width(), snumRect_.height(), background_);
0910 
0911   paint.setFont(statusFont_);
0912   paint.setPen(QColor(255,0,0));
0913   paint.drawText(0, 0, snumRect_.width(), snumRect_.height(),
0914                  Qt::AlignLeft, QString::asprintf("%05d", totalMoves()));
0915 }
0916 
0917 void
0918 PlayField::updatePushesXpm() {
0919   if (!pnumXpm_) return;
0920   if (pnumXpm_->isNull()) return;
0921   //printf("executing PlayField::updatePushesXpm()\n"); 
0922   
0923   QPainter paint(pnumXpm_);
0924   paint.setBrushOrigin(- pnumRect_.x(), - pnumRect_.y());
0925   paint.fillRect(0, 0, pnumRect_.width(), pnumRect_.height(), background_);
0926 
0927   paint.setFont(statusFont_);
0928   paint.setPen(QColor(255,0,0));
0929   paint.drawText(0, 0, pnumRect_.width(), pnumRect_.height(),
0930                  Qt::AlignLeft, QString::asprintf("%05d", totalPushes()));
0931 }
0932 
0933 
0934 void
0935 PlayField::changeAnim(int num)
0936 {
0937   assert(num >= 0 && num <= 3);
0938 
0939   animDelay_ = num;
0940 }
0941 
0942 // FIXME: clean up bookmark stuff
0943 
0944 // static const int bookmark_id[] = {
0945 //   0, 1, 8, 2, 9, 3, 5, 6, 7, 4
0946 // };
0947 
0948 void
0949 PlayField::setBookmark(Bookmark *bm) {
0950   if (!levelMap_->goodLevel()) return;
0951 
0952   if (collection()->id() < 0) {
0953     KMessageBox::error(this, i18n("Sorry, bookmarks for external levels\n"
0954                   "is not implemented yet."));
0955     return;
0956   }
0957 
0958   bm->set(collection()->id(), levelMap_->level(), levelMap_->totalMoves(), history_);
0959 }
0960 
0961 void
0962 PlayField::goToBookmark(Bookmark *bm) {
0963   level(bm->level());
0964   levelChange();
0965   if (!bm->goTo(levelMap_, history_)) fprintf(stderr, "Warning: bad bookmark\n");
0966   //updateLevelXpm();
0967   updateStepsXpm();
0968   updatePushesXpm();
0969   repaint();
0970 }
0971 
0972 bool
0973 PlayField::canMoveNow() {
0974   if (moveInProgress_) return false;
0975   if (!levelMap_->goodLevel()) {
0976     ModalLabel::message(i18n("This level is broken"), this);
0977     return false;
0978   }
0979   return true;
0980 }
0981 
0982 void
0983 PlayField::killTimers() {
0984   for(QList<int>::Iterator it=timers.begin(); it!=timers.end(); it++) {
0985     killTimer(*it);
0986   }
0987   timers.clear();
0988 }