/*************************************************************************** Description : KPuzzle - A KDE Jigsaw Puzzle Game Version : 0.2 Copyright : (C) 2000-2001 by Michael Wand EMail : mwand@gmx.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "main.h" #include "gamewidget.h" #include "kpuzzle.h" #include "kpuzzlegame.h" #include "kpuzzleview.h" #include #include #include #include #include /* Constructor. Parent should be the (single) KPuzzleView object. */ KGameWidget::KGameWidget(QWidget *parent) : QWidget(parent) { _game = NULL; setMouseTracking(true); setCursor(KCursor::blankCursor()); _piecePos = QPoint(-1,-1); _moveTimer = new QTimer(this); connect(_moveTimer,SIGNAL(timeout()),this,SLOT(slotMoveTimer())); _scrollPos = QPoint(0,0); _moveKeyPressed = 0; _rightPressed = false; setBackgroundMode(NoBackground); connect(this,SIGNAL(sigShowAt(QPoint)),this,SLOT(slotShowAt(QPoint))); _backBuffer = NULL; } /* Destructor */ KGameWidget::~KGameWidget() { delete _moveTimer; delete _backBuffer; } /* Initialize some things that cannot be done in the constructor. * Currently, this only concerns the size of the picture which is * the base of the current game. */ void KGameWidget::initialize(QSize picSize) { /* This is necessary to avoid drawing problems */ if (picSize.width() < this->width()) setFixedWidth(picSize.width()); if (picSize.height() < this->height()) setFixedHeight(picSize.height()); _backBuffer = new QPixmap(size()); } /* Basic piece size */ QSize KGameWidget::pieceSize() { return _game->pieceSize(); } /* Size of the displayed piece */ QSize KGameWidget::pieceSizeDisp() { return _game->pieceSizeDisp(); } /* Rectangle of the piece currently moved. The returned QRect * will have -1/-1 as top-left corner if there is no piece * currently moved. */ QRect KGameWidget::pieceRect() { return QRect(pieceTopLeft(),pieceSizeDisp()); } /* Top-left ocrner of the piece currently moved, -1/-1 if there is none */ QPoint KGameWidget::pieceTopLeft() { QPoint sizePt(pieceSize().width() / 2,pieceSize().height() / 2); return piecePos() - sizePt - QPoint(_game->displace(),_game->displace()); } /* Inherited widget event reaction functions. */ void KGameWidget::paintEvent(QPaintEvent* event) { if (((KPuzzleView*) parent())->status() != GS_RUNNING) return; QRect logRect(DPtoLP(event->rect())); ASSERT(_backBuffer->size() == this->size()); bitBlt(_backBuffer,event->rect().topLeft(),_game->gamePixmap(),logRect,CopyROP,false); if (!_rightPressed && piecePos().x() != -1) { QPixmap* pieceTurned = _game->currentPieceTurned(); /* CAUTION: implicit new */ bitBlt(_backBuffer,pieceTopLeft(), pieceTurned,QRect(QPoint(0,0),_game->pieceSizeDisp()),CopyROP,false); delete pieceTurned; } bitBlt(this,event->rect().topLeft(),_backBuffer,event->rect()); } void KGameWidget::mouseMoveEvent(QMouseEvent* event) { if (((KPuzzleView*) parent())->status() != GS_RUNNING) return; if (_rightPressed) { /* panning around */ QPoint diffPos = _movePos - event->pos(); QPoint newPos = scrollPos() + diffPos; if (newPos.x() < 0) newPos.setX(0); if (newPos.x() > _game->pixmapSize().width() - this->width()) newPos.setX( _game->pixmapSize().width() - this->width()); if (newPos.y() < 0) newPos.setY(0); if (newPos.y() > _game->pixmapSize().height() - this->height()) newPos.setY( _game->pixmapSize().height() - this->height()); emit sigShowAt(newPos); _movePos = event->pos(); } /* These are all absolute coordinates */ QPoint prevPos(piecePos()); QRect oldRect(pieceRect()); _piecePos = event->pos(); QRect newRect(pieceRect()); if (prevPos.x() != -1) newRect = newRect.unite(oldRect); update(newRect); } void KGameWidget::mousePressEvent(QMouseEvent* event) { QPoint p; QPoint t; _rightPressed = false; setCursor(KCursor::blankCursor()); switch (event->button()) { case LeftButton: if (((KPuzzleView*) parent())->status() != GS_RUNNING) return; p = DPtoLP(event->pos()); t = calculatePiecePos(p); if (!_game->setPiece(t)) return; break; case RightButton: if (KGlobal::config()->readBoolEntry("RightKeyPansAround",SV_RIGHT_KEY_PANS_AROUND)) { _rightPressed = true; _movePos = event->pos(); setCursor(KCursor::sizeAllCursor()); } else { if (KGlobal::config()->readBoolEntry("TurnPiecesAround",SV_TURN_PIECES_AROUND)) _game->turnCW(); else _game->changeLRTurn(); _game->updateAll(); } break; case MidButton: if (KGlobal::config()->readBoolEntry("TurnPiecesAround",SV_TURN_PIECES_AROUND)) _game->turnCCW(); else _game->changeUDTurn(); _game->updateAll(); break; default: ; } } void KGameWidget::mouseReleaseEvent(QMouseEvent* event) { switch(event->button()) { case RightButton: _rightPressed = false; setCursor(KCursor::blankCursor()); break; default: break; /* To avoid compiler warnings */ } } void KGameWidget::leaveEvent(QEvent*) { if (((KPuzzleView*) parent())->status() != GS_RUNNING) return; QRect rect(pieceRect()); _piecePos = QPoint(-1,-1); update(rect); } /* Returns the logical position of the piece at position p * (that means, if there are m * n pieces in this game, the * return value of this function ranges from (0,0) to (m-1,n-1). */ QPoint KGameWidget::calculatePiecePos(QPoint p) { /* p is in widget coordinates * return value is the logical position of the piece */ QSize ts(_game->pieceSize()); QPoint szpt(ts.width(),ts.height()); QPoint r(p.x() / szpt.x(),p.y() / szpt.y()); return r; } /* Slots activated when moving keys are pressed or released */ void KGameWidget::slotLeftPress() { _moveTimer->start(MOVE_TIMER_INTERVAL); _moveKeyPressed = 1; slotMoveTimer(); } void KGameWidget::slotLeftRelease() { _moveTimer->stop(); _moveKeyPressed = 0; } void KGameWidget::slotRightPress() { _moveTimer->start(MOVE_TIMER_INTERVAL); _moveKeyPressed = 3; slotMoveTimer(); } void KGameWidget::slotRightRelease() { _moveTimer->stop(); _moveKeyPressed = 0; } void KGameWidget::slotUpPress() { _moveTimer->start(MOVE_TIMER_INTERVAL); _moveKeyPressed = 2; slotMoveTimer(); } void KGameWidget::slotUpRelease() { _moveTimer->stop(); _moveKeyPressed = 0; } void KGameWidget::slotDownPress() { slotMoveTimer(); _moveTimer->start(MOVE_TIMER_INTERVAL); _moveKeyPressed = 4; slotMoveTimer(); } void KGameWidget::slotDownRelease() { _moveTimer->stop(); _moveKeyPressed = 0; } /* The timer slot which is in use when a moving key is pressed */ void KGameWidget::slotMoveTimer() { QPoint newPt; switch(_moveKeyPressed) { case 1: if (scrollPos().x() > 0) { newPt = QPoint(scrollPos().x() - 20,scrollPos().y()); if (newPt.x() < 0) newPt.setX(0); emit sigShowAt(newPt); } break; case 2: if (scrollPos().y() > 0) { newPt = QPoint(scrollPos().x(),scrollPos().y() - 20); if (newPt.y() < 0) newPt.setY(0); emit sigShowAt(newPt); } break; case 3: if (scrollPos().x() < _game->pixmapSize().width() - this->width()) { newPt = QPoint(scrollPos().x() + 20,scrollPos().y()); if (newPt.x() > _game->pixmapSize().width() - this->width()) newPt.setX(_game->pixmapSize().width() - this->width()); emit sigShowAt(newPt); } break; case 4: if (scrollPos().y() < _game->pixmapSize().height() - this->height()) { newPt = QPoint(scrollPos().x(),scrollPos().y() + 20); if (newPt.y() > _game->pixmapSize().height() - this->height()) newPt.setY(_game->pixmapSize().height() - this->height()); emit sigShowAt(newPt); } break; } } /* This slot is called to perform a movement of the visible area of the * game area. It is always connected to sigShowAt. */ void KGameWidget::slotShowAt(QPoint pos) { /* To avoid rounding problems */ pos.setX(min(pos.x(),_game->pixmapSize().width() - this->width())); pos.setY(min(pos.y(),_game->pixmapSize().height() - this->height())); scrollPos() = pos; ((KPuzzleView*) parent())->updateButtons(!pos.x() == 0,!pos.y() == 0, pos.x() < _game->pixmapSize().width() - this->width(), pos.y() < _game->pixmapSize().height() - this->height()); update(); }