/*************************************************************************** * Copyright (C) 2008 by Oliver Groß * * z.o.gross@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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "ccrossfieldwidget.h" #include #include #include #include #include #include #include #include #include #include #include namespace qcross { using namespace libqnono; int min(int a, int b) { return (a < b) ? a : b; } int max(int a, int b) { return (a > b) ? a : b; } //public CCrossFieldWidget::CCrossFieldWidget(CNonogram * picture, QWidget * parent) : QWidget(parent), m_Picture(picture), m_OverlayData(NULL), m_MouseMark(CMT_NONE), m_ErrorAware(false), m_NumbersMarkable(true), m_Time(0), m_Paused(true), m_Solved(false), m_TimerId(-1), m_MessageTimeoutId(-1), m_Clock(NULL), m_ErrorCount(0), m_LastErrorMark(-1, -1) { // m_Notifier = new QFrame(this); // m_Notifier->setLineWidth(10); // m_Notifier->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); QPalette notifierPalette(QApplication::palette().color(QPalette::ToolTipBase)); notifierPalette.setColor(QPalette::Text, QApplication::palette().color(QPalette::ToolTipText)); // m_Notifier->setPalette(notifierPalette); // m_Notifier->setAutoFillBackground(true); // QHBoxLayout * notifierLayout = new QHBoxLayout(m_Notifier); // m_NotifierIcon = new QLabel(m_Notifier); // m_NotifierText = new QLabel(m_Notifier); // m_NotifierText->setWordWrap(true); // notifierLayout->addWidget(m_NotifierIcon); // notifierLayout->addWidget(m_NotifierText); // notifierLayout->addStretch(); // m_Notifier->setLayout(notifierLayout); // m_Notifier->hide(); m_Clock = new QLCDNumber(8, this); m_Clock->setVisible(m_Picture); if (m_Picture) { initialize(); m_RemainingPixels = m_Picture->blackPixels(); updateTimeDisplay(); } setEnabled(false); } CCrossFieldWidget::~CCrossFieldWidget() { cleanup(); // delete m_Clock; } void CCrossFieldWidget::setPicture(CNonogram * picture) { if (m_Picture) cleanup(); m_Picture = picture; if (m_Picture) { m_LastErrorMark.setX(-1); m_LastErrorMark.setY(-1); m_ErrorCount = 0; m_Time = picture->timeout(); m_ErrorAware = picture->timeout(); m_Solved = false; updateTimeDisplay(); emit timeChanged(m_Time); initialize(); m_Messages.clear(); nextMessage(); updateMetrics(); m_RemainingPixels = m_Picture->blackPixels(); } m_Clock->setVisible(m_Picture); } void CCrossFieldWidget::showMessage(const QString message, int timeout, MessageType type) { if (!message.isEmpty()) { Message newMessage; newMessage.type = type; newMessage.text = message; newMessage.timeout = timeout; m_Messages.enqueue(newMessage); } if (m_MessageTimeoutId == -1) nextMessage(); } void CCrossFieldWidget::applyState(QDataStream & stream) { qDebug("trying to apply state..."); qDebug("reading basic stuff:"); // error state stream >> m_ErrorAware; qDebug("m_ErrorAware = %i", int(m_ErrorAware)); stream >> m_ErrorCount; qDebug("m_ErrorCount = %i", m_ErrorCount); // time(out) stream >> m_Time; qDebug("m_Time = %i", m_Time); updateTimeDisplay(); emit timeChanged(m_Time); // messages int messageCount; stream >> messageCount; qDebug("messageCount = %i", messageCount); m_Messages.clear(); unsigned char byteBuffer; for (int i = 0; i < messageCount; i++) { qDebug("message #%i:", i); stream >> byteBuffer; qDebug("type = %i", byteBuffer); m_Message.type = MessageType(byteBuffer); if (m_Message.type != Invalid) { stream >> m_Message.timeout; qDebug("timeout = %i", m_Message.timeout); stream >> m_Message.text; qDebug("text = %s", qPrintable(m_Message.text)); m_Messages.enqueue(m_Message); } } nextMessage(); // overlaydata for (int iy = 0; iy < m_Picture->height(); iy++) { for (int ix = 0; ix < m_Picture->width(); ix++) { stream >> byteBuffer; m_OverlayData[ix][iy] = MarkerType(byteBuffer); } } } void CCrossFieldWidget::dumpState(QDataStream & stream) { if (!m_OverlayData || !m_Picture) return; // error state stream << m_ErrorAware; stream << m_ErrorCount; // time(out) stream << m_Time; // messages // actually we should dump the message queue // for now only dump last displayed // TODO dump message queue stream << 1; // dummy message count stream << (unsigned char)(m_Message.type); if (m_Message.type != Invalid) { stream << m_Message.timeout; stream << m_Message.text; } // overlaydata for (int iy = 0; iy < m_Picture->height(); iy++) { for (int ix = 0; ix < m_Picture->width(); ix++) stream << (unsigned char)(m_OverlayData[ix][iy]); } } //public slots void CCrossFieldWidget::mark(int x, int y, int type) { if (type == CMT_NONE || x < 0 || x >= m_Picture->width() || y < 0 || y >= m_Picture->height()) return; MarkerType marker = static_cast(type); execMark(x, y, marker); } void CCrossFieldWidget::setTime(int value) { if (value < 0) value = 0; if (m_Time != value) { m_Time = value; if (m_ErrorAware && !m_Time) { killTimer(m_TimerId); m_TimerId = -1; m_Paused = true; emit timeUp(); } updateTimeDisplay(); emit timeChanged(m_Time); } } void CCrossFieldWidget::setErrorAware(bool value) { m_ErrorAware = value; } void CCrossFieldWidget::setNumbersMarkable(bool value) { m_NumbersMarkable = value; } void CCrossFieldWidget::start() { reset(); resume(); } void CCrossFieldWidget::pause() { if (m_Solved) return; killTimer(m_TimerId); m_TimerId = -1; if (m_Message.type != Invalid) { m_Messages.enqueue(m_Message); if (m_MessageTimeoutId != -1) { killTimer(m_MessageTimeoutId); m_MessageTimeoutId = -1; } } m_Paused = true; setEnabled(false); } void CCrossFieldWidget::resume() { if (m_Solved) return; m_Paused = false; setEnabled(true); update(); m_TimerId = startTimer(1000); } //protected void CCrossFieldWidget::nextMessage() { if (m_MessageTimeoutId != -1) killTimer(m_MessageTimeoutId); m_MessageTimeoutId = -1; if (m_Messages.isEmpty()) { m_Message.type = Invalid; // m_Notifier->hide(); // updateMetrics(); update(); return; } m_Message = m_Messages.dequeue(); update(); /* switch (m_Message.type) { case Information: m_NotifierIcon->setPixmap(style()->standardIcon(QStyle::SP_MessageBoxInformation).pixmap(22, 22)); break; case Warning: m_NotifierIcon->setPixmap(style()->standardIcon(QStyle::SP_MessageBoxWarning).pixmap(22, 22)); break; case Critical: m_NotifierIcon->setPixmap(style()->standardIcon(QStyle::SP_MessageBoxCritical).pixmap(22, 22)); break; default: break; } m_NotifierText->setText(m_Message.text); m_Notifier->show(); updateMetrics();*/ if (m_Message.timeout) m_MessageTimeoutId = startTimer(m_Message.timeout); } void CCrossFieldWidget::initialize() { m_OverlayData = new MarkerType *[m_Picture->width()]; for (int i = 0; i < m_Picture->width(); i++) { m_OverlayData[i] = new MarkerType[m_Picture->height()]; for (int j = 0; j < m_Picture->height(); j++) m_OverlayData[i][j] = CMT_UNMARKED; } int minimumW = max(m_Picture->maximumNumberCount()+1, 5); int minimumH = minimumW; minimumW += m_Picture->width(); minimumH += m_Picture->height(); int minimumBoxSize = max(fontMetrics().width(QString::number(m_Picture->height())), fontMetrics().width(QString::number(m_Picture->width()))); minimumBoxSize = max(minimumBoxSize, fontInfo().pixelSize() * 1.5); minimumW *= minimumBoxSize; minimumH *= minimumBoxSize; setMinimumSize(minimumW, minimumH); } void CCrossFieldWidget::cleanup() { qDebug("m_TimerId = %i m_MessageTimeoutId = %i", m_TimerId, m_MessageTimeoutId); if (m_TimerId != -1) { killTimer(m_TimerId); m_TimerId = -1; } if (m_MessageTimeoutId != -1) { killTimer(m_MessageTimeoutId); m_MessageTimeoutId = -1; } if (m_OverlayData) { for (int i = 0; i < m_Picture->width(); i++) delete[] m_OverlayData[i]; delete[] m_OverlayData; m_OverlayData = NULL; } } void CCrossFieldWidget::reset() { m_Messages.clear(); nextMessage(); for (int i = 0; i < m_Picture->width(); i++) { for (int j = 0; j < m_Picture->height(); j++) m_OverlayData[i][j] = CMT_UNMARKED; } m_Solved = false; m_ErrorCount = 0; m_LastErrorMark.setX(-1); m_LastErrorMark.setY(-1); m_RemainingPixels = m_Picture->blackPixels(); } bool CCrossFieldWidget::checkNoError(int x, int y) { return (m_OverlayData[x][y] == CMT_MARKED) || m_Picture->pixel(x, y); } void CCrossFieldWidget::timerEvent(QTimerEvent * event) { if (event->timerId() == m_TimerId) setTime(m_Time + (m_ErrorAware ? -1 : +1)); else if (event->timerId() == m_MessageTimeoutId) nextMessage(); } void CCrossFieldWidget::updateTimeDisplay() { quint8 seconds = m_Time % 60; quint8 minutes = m_Time / 60; quint8 hours = minutes / 60; minutes %= 60; QString time(""); if (hours) time += QString::number(hours) + ':'; if (minutes < 10) time += '0'; time += QString::number(minutes) + ':'; if (seconds < 10) time += '0'; time += QString::number(seconds); m_Clock->display(time); } void CCrossFieldWidget::execMark(int x, int y, MarkerType & marker) { switch (marker) { case CMT_MARKED: if (m_Picture->pixel(x, y)) { m_RemainingPixels--; m_OverlayData[x][y] = marker; } else { m_ErrorCount++; if (m_ErrorAware) { m_OverlayData[x][y] = CMT_CROSSED; marker = CMT_NONE; m_LastErrorMark.setX(x); m_LastErrorMark.setY(y); emit markError(); } else m_OverlayData[x][y] = marker; } break; case CMT_CROSSED: case CMT_UNMARKED: if (m_ErrorAware && x == m_LastErrorMark.x() && y == m_LastErrorMark.y()) { m_LastErrorMark.setX(-1); m_LastErrorMark.setY(-1); } if (m_OverlayData[x][y] == CMT_MARKED) { if (m_Picture->pixel(x, y)) m_RemainingPixels++; else if (!m_ErrorAware) m_ErrorCount--; } m_OverlayData[x][y] = marker; break; default: break; } if (!m_RemainingPixels && (m_ErrorAware || !m_ErrorCount)) { killTimer(m_TimerId); m_TimerId = -1; m_Paused = true; m_Solved = true; emit solved(); } } void CCrossFieldWidget::paintEvent(QPaintEvent *) { QPainter painter(this); if (!m_Picture) { /* painter.drawText(originX, originY, m_BoxSize, m_BoxSize, Qt::AlignVCenter | Qt::AlignCenter, QString::number(m_Picture->rowNumbers(i)[m_Picture->rowNumbers(i).size() - j]));*/ return; } int originX = m_OffsetX + m_HeaderWidth; int originY = m_OffsetY + m_HeaderHeight; int gridWidth = m_RasterWidth - m_HeaderWidth; int gridHeight = m_RasterHeight - m_HeaderHeight; // draw background { const int delta = 5 * m_BoxSize; int offsetX = 0; int offsetY; int deltaX = delta; int deltaY; bool useBaseX; bool useBaseY = true; while (offsetX < gridWidth) { if (gridWidth - offsetX < delta) deltaX = gridWidth - offsetX; offsetY = 0; deltaY = delta; useBaseX = useBaseY; while (offsetY < gridHeight) { if (gridHeight - offsetY < delta) deltaY = gridHeight - offsetY; painter.fillRect(originX + offsetX, originY + offsetY, deltaX, deltaY, palette().color(useBaseX ? QPalette::Base : QPalette::AlternateBase)); useBaseX = !useBaseX; offsetY += deltaY; } useBaseY = !useBaseY; offsetX += deltaX; } } // draw markers and crosses { QPen pen; pen.setWidth(m_MarkerSize / 8); painter.setPen(pen); QRectF markerRect(m_MarkerOffset, m_MarkerOffset, m_MarkerSize, m_MarkerSize); QColor markerColor = palette().color(QPalette::Highlight); QColor errorColor = markerColor; errorColor.setAlpha(128); painter.setBrush(markerColor); for (int i = 0; i < m_Picture->width(); i++) { originX = m_OffsetX + m_HeaderWidth + i * m_BoxSize; markerRect.moveLeft(originX + m_MarkerOffset); for (int j = 0; j < m_Picture->height(); j++) { originY = m_OffsetY + m_HeaderHeight + j * m_BoxSize; markerRect.moveTop(originY + m_MarkerOffset); switch (m_OverlayData[i][j]) { case CMT_MARKED: painter.fillRect(markerRect, painter.brush()); break; case CMT_CROSSED: if (m_Solved) break; if (m_ErrorAware && m_ErrorCount && i == m_LastErrorMark.x() && j == m_LastErrorMark.y()) { painter.setBrush(errorColor); painter.fillRect(markerRect, painter.brush()); painter.setBrush(markerColor); } painter.drawLine(markerRect.topLeft(), markerRect.bottomRight()); painter.drawLine(markerRect.bottomLeft(), markerRect.topRight()); break; default: break; } } } } // draw grid lines painter.setPen(palette().color(QPalette::Dark)); for (int i = m_OffsetX + m_HeaderWidth; i < m_OffsetX + m_RasterWidth; i += m_BoxSize) { painter.drawLine(i, m_OffsetY + m_HeaderHeight, i, m_OffsetY + m_RasterHeight-1); } for (int i = m_OffsetY + m_HeaderHeight; i < m_OffsetY + m_RasterHeight; i += m_BoxSize) { painter.drawLine(m_OffsetX + m_HeaderWidth, i, m_OffsetX + m_RasterWidth-1, i); } // draw numbers area if (m_Paused) painter.setPen(palette().color(QPalette::Shadow)); QFont font = painter.font(); font.setPixelSize(m_MarkerSize * 0.9); painter.setFont(font); for (int i = 0; i < m_Picture->width(); i++) { originX = m_OffsetX + m_HeaderWidth + i * m_BoxSize; painter.fillRect(originX, m_OffsetY, m_BoxSize, m_HeaderHeight, palette().color((i % 2) ? QPalette::AlternateBase : QPalette::Base)); if (!m_Paused) { painter.setPen(palette().color(QPalette::WindowText)); for (int j = 1; j <= m_Picture->columnNumbers(i).size(); j++) { originY = m_OffsetY + m_HeaderHeight - j * m_BoxSize; painter.drawText(originX, originY, m_BoxSize, m_BoxSize, Qt::AlignVCenter | Qt::AlignCenter, QString::number(m_Picture->columnNumbers(i)[m_Picture->columnNumbers(i).size() - j])); } painter.setPen(palette().color(QPalette::Shadow)); } if (!(i % 5)) painter.drawLine(originX, m_OffsetY, originX, m_OffsetY + m_RasterHeight-1); } // painter.drawLine(m_OffsetX + m_RasterWidth, m_OffsetY, m_OffsetX + m_RasterWidth, m_OffsetY + m_RasterHeight); for (int i = 0; i < m_Picture->height(); i++) { originY = m_OffsetY + m_HeaderHeight + i * m_BoxSize; painter.fillRect(m_OffsetX, originY, m_HeaderWidth, m_BoxSize, palette().color((i % 2) ? QPalette::AlternateBase : QPalette::Base)); if (!m_Paused) { painter.setPen(palette().color(QPalette::WindowText)); for (int j = 1; j <= m_Picture->rowNumbers(i).size(); j++) { originX = m_OffsetX + m_HeaderWidth - j * m_BoxSize; painter.drawText(originX, originY, m_BoxSize, m_BoxSize, Qt::AlignVCenter | Qt::AlignCenter, QString::number(m_Picture->rowNumbers(i)[m_Picture->rowNumbers(i).size() - j])); } painter.setPen(palette().color(QPalette::Shadow)); } if (!(i % 5)) painter.drawLine(m_OffsetX, originY, m_OffsetX + m_RasterWidth-1, originY); } // draw message if needed if (m_Message.type != Invalid) { QRect boxrect(m_HeaderWidth + m_OffsetX, m_HeaderHeight + m_OffsetY, m_RasterWidth - m_HeaderWidth, m_RasterHeight - m_HeaderHeight); QColor fillColor = palette().color(QPalette::Base); fillColor.setAlpha(220); painter.fillRect(boxrect, fillColor); font.setPixelSize(font.pixelSize() * 2); painter.setFont(font); painter.drawText(boxrect, Qt::AlignVCenter | Qt::AlignCenter | Qt::TextWordWrap, m_Message.text); } } /* void CCrossFieldWidget::updateCell(int x, int y) { QPainter painter(this); int originX, originY; originX = m_OffsetX + m_HeaderWidth + x * m_BoxSize; originY = m_OffsetY + m_HeaderHeight + y * m_BoxSize; painter.fillRect(originX + 1, originY + 1, m_BoxSize - 2, m_BoxSize - 2, palette().color(QPalette::Base)); switch (m_OverlayData[x][y]) { case CMT_MARKED: painter.drawRect(originX + 2, originY + 2, m_BoxSize - 4, m_BoxSize - 4); break; case CMT_CROSSED: painter.drawLine(originX + 2, originY + 2, originX + m_BoxSize - 2, originY + m_BoxSize - 2); painter.drawLine(originX + 2, originY + m_BoxSize - 2, originX + m_BoxSize - 2, originY + 2); break; default: break; } }*/ void CCrossFieldWidget::resizeEvent(QResizeEvent * /*event*/) { if (!m_Picture) return; updateMetrics(); } void CCrossFieldWidget::updateMetrics() { m_HeaderWidth = max(m_Picture->maximumNumberCount()+1, 5); /* m_HeaderWidth = max(m_Picture->width() / 2 + 1, m_Picture->height() / 2 + 1); m_HeaderWidth = m_Picture->width() / 2 + 1; m_HeaderHeight = m_Picture->height() / 2 + 1;*/ int fieldsize = min((m_Picture->width() + m_HeaderWidth), (m_Picture->height() + m_HeaderWidth)); int widgetsize = min(width(), height()); m_BoxSize = ((double)widgetsize / fieldsize); /*min((double)width() / (m_Picture->width() + m_HeaderWidth), (double)height() / (m_Picture->height() + m_HeaderWidth));*/ m_MarkerSize = m_BoxSize * 2.0/3.0; m_MarkerOffset = ((m_BoxSize - m_MarkerSize) / 2.0); m_HeaderWidth *= m_BoxSize; m_HeaderHeight = m_HeaderWidth; m_RasterWidth = m_BoxSize * m_Picture->width() + m_HeaderWidth; m_RasterHeight = m_BoxSize * m_Picture->height() + m_HeaderHeight; m_OffsetX = (width() - m_RasterWidth) / 2; m_OffsetY = (height() - m_RasterHeight) / 2; int clockHeight = /*m_Notifier->isVisible() ? m_HeaderHeight - m_Notifier->sizeHint().height()*/ /* * 3.0 / 4.0 */ /*:*/ m_HeaderHeight; if (m_Clock) m_Clock->setGeometry(m_OffsetX, m_OffsetY, m_HeaderWidth, clockHeight); // if (m_Notifier && m_Notifier->isVisible()) // m_Notifier->setGeometry(m_OffsetX, m_OffsetY + clockHeight, m_HeaderWidth, m_Notifier->sizeHint().height()); } void CCrossFieldWidget::mousePressEvent(QMouseEvent * event) { if (m_Paused) return; int pressedX = event->x() - m_OffsetX; int pressedY = event->y() - m_OffsetY; if (pressedX < m_HeaderWidth || pressedY < m_HeaderHeight || pressedX >= m_RasterWidth || pressedY >= m_RasterHeight) return; pressedX = (pressedX - m_HeaderWidth) / m_BoxSize; pressedY = (pressedY - m_HeaderHeight) / m_BoxSize; if (event->button() == Qt::RightButton) m_MouseMark = CMT_CROSSED; else m_MouseMark = CMT_MARKED; if (m_OverlayData[pressedX][pressedY] == m_MouseMark) m_MouseMark = CMT_UNMARKED; execMark(pressedX, pressedY, m_MouseMark); update(); } void CCrossFieldWidget::mouseMoveEvent(QMouseEvent * event) { if (m_Paused) return; if (m_MouseMark == CMT_NONE) return; int pressedX = event->x() - m_OffsetX; int pressedY = event->y() - m_OffsetY; if (pressedX < m_HeaderWidth || pressedY < m_HeaderHeight || pressedX >= m_RasterWidth || pressedY >= m_RasterHeight) return; pressedX = (pressedX - m_HeaderWidth) / m_BoxSize; pressedY = (pressedY - m_HeaderHeight) / m_BoxSize; if (m_OverlayData[pressedX][pressedY] != m_MouseMark) { execMark(pressedX, pressedY, m_MouseMark); update(); } } void CCrossFieldWidget::mouseReleaseEvent(QMouseEvent *) { m_MouseMark = CMT_NONE; } }