/* * Copyright (C) 2020-2024 Gavin MacGregor * * This file is part of QTeletextMaker. * * QTeletextMaker 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 3 of the License, or * (at your option) any later version. * * QTeletextMaker 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 QTeletextMaker. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mainwidget.h" #include "decode.h" #include "document.h" #include "keymap.h" #include "levelonecommands.h" #include "levelonepage.h" #include "render.h" #include "x26triplets.h" TeletextWidget::TeletextWidget(QFrame *parent) : QFrame(parent) { this->resize(QSize(480, 250)); this->setAttribute(Qt::WA_NoSystemBackground); this->setAttribute(Qt::WA_InputMethodEnabled, true); m_teletextDocument = new TeletextDocument(); m_levelOnePage = m_teletextDocument->currentSubPage(); m_pageDecode.setTeletextPage(m_levelOnePage); m_pageRender.setDecoder(&m_pageDecode); m_insertMode = false; m_selectionInProgress = false; setFocusPolicy(Qt::StrongFocus); m_flashTiming = m_flashPhase = 0; connect(&m_pageRender, &TeletextPageRender::flashChanged, this, &TeletextWidget::updateFlashTimer); connect(&m_pageDecode, &TeletextPageDecode::sidePanelsChanged, this, &TeletextWidget::changeSize); connect(m_teletextDocument, &TeletextDocument::subPageSelected, this, &TeletextWidget::subPageSelected); connect(m_teletextDocument, &TeletextDocument::contentsChanged, this, &TeletextWidget::refreshPage); connect(m_teletextDocument, &TeletextDocument::colourChanged, &m_pageRender, &TeletextPageRender::colourChanged); } TeletextWidget::~TeletextWidget() { if (m_flashTimer.isActive()) m_flashTimer.stop(); delete m_teletextDocument; } // Re-implemented so compose/dead keys work properly void TeletextWidget::inputMethodEvent(QInputMethodEvent* event) { if (!event->commitString().isEmpty()) { QKeyEvent keyEvent(QEvent::KeyPress, 0, Qt::NoModifier, event->commitString()); keyPressEvent(&keyEvent); } event->accept(); } void TeletextWidget::subPageSelected() { m_levelOnePage = m_teletextDocument->currentSubPage(); m_pageDecode.setTeletextPage(m_levelOnePage); m_pageDecode.decodePage(); m_pageRender.renderPage(true); update(); } void TeletextWidget::refreshPage() { m_pageDecode.decodePage(); update(); } void TeletextWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter widgetPainter(this); m_pageRender.renderPage(); widgetPainter.drawPixmap(m_pageDecode.leftSidePanelColumns()*12, 0, *m_pageRender.pagePixmap(m_flashPhase), 0, 0, 480, 250); if (m_pageDecode.leftSidePanelColumns()) widgetPainter.drawPixmap(0, 0, *m_pageRender.pagePixmap(m_flashPhase), 864-m_pageDecode.leftSidePanelColumns()*12, 0, m_pageDecode.leftSidePanelColumns()*12, 250); if (m_pageDecode.rightSidePanelColumns()) widgetPainter.drawPixmap(480+m_pageDecode.leftSidePanelColumns()*12, 0, *m_pageRender.pagePixmap(m_flashPhase), 480, 0, m_pageDecode.rightSidePanelColumns()*12, 250); } void TeletextWidget::updateFlashTimer(int newFlashTimer) { m_flashTiming = newFlashTimer; m_flashPhase = 0; if (newFlashTimer == 0) { m_flashTimer.stop(); update(); return; } m_flashTimer.start((newFlashTimer == 1) ? 500 : 167, this); } void TeletextWidget::timerEvent(QTimerEvent *event) { if (event->timerId() == m_flashTimer.timerId()) { if (m_flashTiming == 1) m_flashPhase += 3; else m_flashPhase++; if (m_flashPhase == 6) m_flashPhase = 0; update(); } else QWidget::timerEvent(event); } void TeletextWidget::pauseFlash(bool pauseNow) { if (pauseNow && m_flashTiming != 0) { m_flashTimer.stop(); m_flashPhase = 0; update(); } else if (m_flashTiming != 0) m_flashTimer.start((m_flashTiming == 1) ? 500 : 167, this); } void TeletextWidget::setInsertMode(bool insertMode) { m_insertMode = insertMode; } void TeletextWidget::setReveal(bool reveal) { m_pageRender.setReveal(reveal); update(); } void TeletextWidget::setMix(bool mix) { m_pageRender.setMix(mix); update(); } void TeletextWidget::setShowControlCodes(bool showControlCodes) { m_pageRender.setShowControlCodes(showControlCodes); update(); } void TeletextWidget::setControlBit(int bitNumber, bool active) { m_levelOnePage->setControlBit(bitNumber, active); if (bitNumber == 1 || bitNumber == 2) { m_pageDecode.decodePage(); m_pageRender.renderPage(true); update(); } } void TeletextWidget::setDefaultCharSet(int newDefaultCharSet) { m_levelOnePage->setDefaultCharSet(newDefaultCharSet); } void TeletextWidget::setDefaultNOS(int newDefaultNOS) { m_levelOnePage->setDefaultNOS(newDefaultNOS); } void TeletextWidget::setSidePanelWidths(int newLeftSidePanelColumns, int newRightSidePanelColumns) { m_levelOnePage->setLeftSidePanelDisplayed(newLeftSidePanelColumns != 0); m_levelOnePage->setRightSidePanelDisplayed(newRightSidePanelColumns != 0); if (newLeftSidePanelColumns) m_levelOnePage->setSidePanelColumns((newLeftSidePanelColumns == 16) ? 0 : newLeftSidePanelColumns); else m_levelOnePage->setSidePanelColumns((newRightSidePanelColumns == 0) ? 0 : 16-newRightSidePanelColumns); m_pageDecode.updateSidePanels(); } void TeletextWidget::setSidePanelAtL35Only(bool newSidePanelAtL35Only) { m_levelOnePage->setSidePanelStatusL25(!newSidePanelAtL35Only); m_pageDecode.updateSidePanels(); } void TeletextWidget::changeSize() { setFixedSize(QSize(480+(pageDecode()->leftSidePanelColumns()+pageDecode()->rightSidePanelColumns())*12, 250)); emit sizeChanged(); } void TeletextWidget::keyPressEvent(QKeyEvent *event) { if (event->key() < 0x01000000) { // A character-typing key was pressed // Try to keymap it, if not keymapped then plain ASCII code (may be) returned char mappedKeyPress = keymapping[m_pageDecode.level1CharSet(m_teletextDocument->cursorRow(), m_teletextDocument->cursorColumn())].value(event->text().at(0), *qPrintable(event->text().at(0))); if (mappedKeyPress >= 0x00 && mappedKeyPress <= 0x1f) return; // If outside ASCII map then the character can't be represented by current Level 1 character set // Map it to block character so it doesn't need to be inserted-between later on if (mappedKeyPress & 0x80) mappedKeyPress = 0x7f; if (m_pageDecode.level1MosaicAttribute(m_teletextDocument->cursorRow(), m_teletextDocument->cursorColumn()) && (mappedKeyPress < 0x40 || mappedKeyPress > 0x5f)) { // We're on a mosaic and a blast-through character was NOT pressed if (event->key() >= Qt::Key_0 && event->key() <= Qt::Key_9 && event->modifiers() & Qt::KeypadModifier) { switch (event->key()) { case Qt::Key_7: toggleCharacterBit(0x01); // Top left break; case Qt::Key_8: toggleCharacterBit(0x02); // Top right break; case Qt::Key_4: toggleCharacterBit(0x04); // Middle left break; case Qt::Key_5: toggleCharacterBit(0x08); // Middle right break; case Qt::Key_1: toggleCharacterBit(0x10); // Bottom left break; case Qt::Key_2: toggleCharacterBit(0x40); // Bottom right break; case Qt::Key_9: toggleCharacterBit(0x5f); // Invert break; case Qt::Key_6: toggleCharacterBit(0x7f); // Set all break; case Qt::Key_3: toggleCharacterBit(0x20); // Clear all break; case Qt::Key_0: toggleCharacterBit(0x66); // Dither break; } return; } if (event->key() == Qt::Key_Space) { setCharacter(0x20); return; } // This macro is defined in keymap.h if no native scan codes are defined // for the platform we are compiling on #ifndef QTTM_NONATIVESCANCODES if (event->nativeScanCode() > 1) { switch (event->nativeScanCode()) { case mosaicNativeScanCodes[0]: toggleCharacterBit(0x01); // Top left break; case mosaicNativeScanCodes[1]: toggleCharacterBit(0x02); // Top right break; case mosaicNativeScanCodes[2]: toggleCharacterBit(0x04); // Middle left break; case mosaicNativeScanCodes[3]: toggleCharacterBit(0x08); // Middle right break; case mosaicNativeScanCodes[4]: toggleCharacterBit(0x10); // Bottom left break; case mosaicNativeScanCodes[5]: toggleCharacterBit(0x40); // Bottom right break; case mosaicNativeScanCodes[6]: toggleCharacterBit(0x5f); // Invert break; case mosaicNativeScanCodes[7]: toggleCharacterBit(0x7f); // Set all break; case mosaicNativeScanCodes[8]: toggleCharacterBit(0x20); // Clear all break; case mosaicNativeScanCodes[9]: toggleCharacterBit(0x66); // Dither break; } return; } else qDebug("nativeScanCode not usable! Please use numeric keypad to toggle mosaic bits."); #else qDebug("nativeScanCode was not compiled in! Please use numeric keypad to toggle mosaic bits."); #endif // TODO some contingency plan if nativeScanCode didn't work? return; } setCharacter(mappedKeyPress); return; } switch (event->key()) { case Qt::Key_Backspace: m_teletextDocument->undoStack()->push(new BackspaceKeyCommand(m_teletextDocument, m_insertMode)); break; case Qt::Key_Tab: m_teletextDocument->undoStack()->push(new TypeCharacterCommand(m_teletextDocument, 0x20, true)); break; case Qt::Key_Delete: m_teletextDocument->undoStack()->push(new DeleteKeyCommand(m_teletextDocument)); break; case Qt::Key_Insert: emit insertKeyPressed(); break; case Qt::Key_Up: m_teletextDocument->cursorUp(event->modifiers() & Qt::ShiftModifier); break; case Qt::Key_Down: m_teletextDocument->cursorDown(event->modifiers() & Qt::ShiftModifier); break; case Qt::Key_Left: m_teletextDocument->cursorLeft(event->modifiers() & Qt::ShiftModifier); break; case Qt::Key_Right: m_teletextDocument->cursorRight(event->modifiers() & Qt::ShiftModifier); break; case Qt::Key_Return: case Qt::Key_Enter: m_teletextDocument->cursorDown(); m_teletextDocument->moveCursor(m_teletextDocument->cursorRow(), 0); break; case Qt::Key_Home: m_teletextDocument->moveCursor(m_teletextDocument->cursorRow(), 0, event->modifiers() & Qt::ShiftModifier); break; case Qt::Key_End: m_teletextDocument->moveCursor(m_teletextDocument->cursorRow(), 39, event->modifiers() & Qt::ShiftModifier); break; case Qt::Key_PageUp: m_teletextDocument->selectSubPageNext(); break; case Qt::Key_PageDown: m_teletextDocument->selectSubPagePrevious(); break; case Qt::Key_F6: m_pageDecode.decodePage(); m_pageRender.renderPage(true); update(); break; default: QFrame::keyPressEvent(event); } } void TeletextWidget::setCharacter(unsigned char newCharacter) { m_teletextDocument->undoStack()->push(new TypeCharacterCommand(m_teletextDocument, newCharacter, m_insertMode)); } void TeletextWidget::toggleCharacterBit(unsigned char bitToToggle) { m_teletextDocument->undoStack()->push(new ToggleMosaicBitCommand(m_teletextDocument, bitToToggle)); } void TeletextWidget::selectionToClipboard() { QByteArray nativeData; QString plainTextData; QClipboard *clipboard = QApplication::clipboard(); nativeData.resize(2 + m_teletextDocument->selectionWidth() * m_teletextDocument->selectionHeight()); nativeData[0] = m_teletextDocument->selectionHeight(); nativeData[1] = m_teletextDocument->selectionWidth(); plainTextData.reserve((m_teletextDocument->selectionWidth()+1) * m_teletextDocument->selectionHeight() - 1); int i=2; for (int r=m_teletextDocument->selectionTopRow(); r<=m_teletextDocument->selectionBottomRow(); r++) { for (int c=m_teletextDocument->selectionLeftColumn(); c<=m_teletextDocument->selectionRightColumn(); c++) { nativeData[i++] = m_teletextDocument->currentSubPage()->character(r, c); if (m_teletextDocument->currentSubPage()->character(r, c) >= 0x20) plainTextData.append(keymapping[m_pageDecode.level1CharSet(r, c)].key(m_teletextDocument->currentSubPage()->character(r, c), QChar(m_teletextDocument->currentSubPage()->character(r, c)))); else plainTextData.append(' '); } plainTextData.append('\n'); } QMimeData *mimeData = new QMimeData(); mimeData->setData("application/x-teletext", nativeData); mimeData->setText(plainTextData); clipboard->setMimeData(mimeData); } void TeletextWidget::cut() { if (!m_teletextDocument->selectionActive()) return; selectionToClipboard(); m_teletextDocument->undoStack()->push(new CutCommand(m_teletextDocument)); } void TeletextWidget::copy() { if (!m_teletextDocument->selectionActive()) return; selectionToClipboard(); } void TeletextWidget::paste() { m_teletextDocument->undoStack()->push(new PasteCommand(m_teletextDocument, m_pageDecode.level1CharSet(m_teletextDocument->cursorRow(), m_teletextDocument->cursorColumn()))); } QPair TeletextWidget::mouseToRowAndColumn(const QPoint &mousePosition) { int row = mousePosition.y() / 10; int column = mousePosition.x() / 12 - m_pageDecode.leftSidePanelColumns(); if (row < 1) row = 1; if (row > 24) row = 24; if (column < 0) column = 0; if (column > 39) column = 39; return qMakePair(row, column); } void TeletextWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { m_teletextDocument->cancelSelection(); QPair position = mouseToRowAndColumn(event->pos()); m_teletextDocument->moveCursor(position.first, position.second); update(); } } void TeletextWidget::mouseMoveEvent(QMouseEvent *event) { if (event->buttons() & Qt::LeftButton) { QPair position = mouseToRowAndColumn(event->pos()); if (position.first != m_teletextDocument->cursorRow() || position.second != m_teletextDocument->cursorColumn()) { if (!m_selectionInProgress) { m_selectionInProgress = true; m_teletextDocument->setSelectionCorner(m_teletextDocument->cursorRow(), m_teletextDocument->cursorColumn()); } m_teletextDocument->moveCursor(position.first, position.second, true); } } } void TeletextWidget::mouseReleaseEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) m_selectionInProgress = false; } void TeletextWidget::focusInEvent(QFocusEvent *event) { QFrame::focusInEvent(event); } void TeletextWidget::focusOutEvent(QFocusEvent *event) { QFrame::focusOutEvent(event); } LevelOneScene::LevelOneScene(QWidget *levelOneWidget, QObject *parent) : QGraphicsScene(parent) { m_grid = false; // These dimensions are scratch, setBorderDimensions will get called straight away to adjust them setSceneRect(0, 0, 600, 288); // Full screen colours m_fullScreenTopRectItem = new QGraphicsRectItem(0, 0, 600, 19); m_fullScreenTopRectItem->setPen(Qt::NoPen); m_fullScreenTopRectItem->setBrush(QBrush(QColor(0, 0, 0))); addItem(m_fullScreenTopRectItem); m_fullScreenBottomRectItem = new QGraphicsRectItem(0, 269, 600, 19); m_fullScreenBottomRectItem->setPen(Qt::NoPen); m_fullScreenBottomRectItem->setBrush(QBrush(QColor(0, 0, 0))); addItem(m_fullScreenBottomRectItem); // Full row colours for (int r=0; r<25; r++) { m_fullRowLeftRectItem[r] = new QGraphicsRectItem(0, 19+r*10, 60, 10); m_fullRowLeftRectItem[r]->setPen(Qt::NoPen); m_fullRowLeftRectItem[r]->setBrush(QBrush(QColor(0, 0, 0))); addItem(m_fullRowLeftRectItem[r]); m_fullRowRightRectItem[r] = new QGraphicsRectItem(540, 19+r*10, 60, 10); m_fullRowRightRectItem[r]->setPen(Qt::NoPen); m_fullRowRightRectItem[r]->setBrush(QBrush(QColor(0, 0, 0))); addItem(m_fullRowRightRectItem[r]); } // Main text widget m_levelOneProxyWidget = addWidget(levelOneWidget); m_levelOneProxyWidget->setPos(60, 19); m_levelOneProxyWidget->setAutoFillBackground(false); m_levelOneProxyWidget->setFocus(); // Selection m_selectionRectItem = new QGraphicsRectItem(0, 0, 12, 10); m_selectionRectItem->setVisible(false); m_selectionRectItem->setPen(QPen(QColor(192, 192, 192), 1, Qt::DashLine)); m_selectionRectItem->setBrush(QBrush(QColor(255, 255, 255, 64))); addItem(m_selectionRectItem); // Cursor m_cursorRectItem = new QGraphicsRectItem(0, 0, 12, 10); m_cursorRectItem->setPen(Qt::NoPen); m_cursorRectItem->setBrush(QBrush(QColor(128, 128, 128, 192))); addItem(m_cursorRectItem); // Optional grid overlay for text widget m_mainGridItemGroup = new QGraphicsItemGroup; m_mainGridItemGroup->setVisible(false); addItem(m_mainGridItemGroup); // Additional vertical pieces of grid for side panels for (int i=0; i<32; i++) { m_sidePanelGridNeeded[i] = false; m_sidePanelGridItemGroup[i] = new QGraphicsItemGroup; m_sidePanelGridItemGroup[i]->setVisible(false); addItem(m_sidePanelGridItemGroup[i]); } for (int r=1; r<25; r++) { for (int c=0; c<40; c++) { QGraphicsRectItem *gridPiece = new QGraphicsRectItem(c*12, r*10, 12, 10); gridPiece->setPen(QPen(QBrush(QColor(128, 128, 128, r<24 ? 192 : 128)), 0)); m_mainGridItemGroup->addToGroup(gridPiece); } if (r < 24) for (int c=0; c<32; c++) { QGraphicsRectItem *gridPiece = new QGraphicsRectItem(0, r*10, 12, 10); gridPiece->setPen(QPen(QBrush(QColor(128, 128, 128, 64)), 0)); m_sidePanelGridItemGroup[c]->addToGroup(gridPiece); } } installEventFilter(this); } void LevelOneScene::setBorderDimensions(int sceneWidth, int sceneHeight, int widgetWidth, int leftSidePanelColumns, int rightSidePanelColumns) { setSceneRect(0, 0, sceneWidth, sceneHeight); // Assume text widget height is always 250 int topBottomBorders = (sceneHeight-250) / 2; // Ideally we'd use m_levelOneProxyWidget->size() to discover the widget width ourselves // but this causes a stubborn segfault, so we have to receive the widgetWidth as a parameter int leftRightBorders = (sceneWidth-widgetWidth) / 2; m_levelOneProxyWidget->setPos(leftRightBorders, topBottomBorders); // Position grid to cover central 40 columns m_mainGridItemGroup->setPos(leftRightBorders + leftSidePanelColumns*12, topBottomBorders); updateCursor(); updateSelection(); // Grid for right side panel for (int c=0; c<16; c++) if (rightSidePanelColumns > c) { m_sidePanelGridItemGroup[c]->setPos(leftRightBorders + leftSidePanelColumns*12 + 480 + c*12, topBottomBorders); m_sidePanelGridItemGroup[c]->setVisible(m_grid); m_sidePanelGridNeeded[c] = true; } else { m_sidePanelGridItemGroup[c]->setVisible(false); m_sidePanelGridNeeded[c] = false; } // Grid for left side panel for (int c=0; c<16; c++) if (c < leftSidePanelColumns) { m_sidePanelGridItemGroup[31-c]->setPos(leftRightBorders + (leftSidePanelColumns-c-1)*12, topBottomBorders); m_sidePanelGridItemGroup[31-c]->setVisible(m_grid); m_sidePanelGridNeeded[31-c] = true; } else { m_sidePanelGridItemGroup[31-c]->setVisible(false); m_sidePanelGridNeeded[31-c] = false; } // Full screen colours m_fullScreenTopRectItem->setRect(0, 0, sceneWidth, topBottomBorders); m_fullScreenBottomRectItem->setRect(0, 250+topBottomBorders, sceneWidth, topBottomBorders); // Full row colours for (int r=0; r<25; r++) { m_fullRowLeftRectItem[r]->setRect(0, topBottomBorders+r*10, leftRightBorders+1, 10); m_fullRowRightRectItem[r]->setRect(sceneWidth-leftRightBorders-1, topBottomBorders+r*10, leftRightBorders+1, 10); } } void LevelOneScene::updateCursor() { m_cursorRectItem->setPos(m_mainGridItemGroup->pos().x() + static_cast(m_levelOneProxyWidget->widget())->document()->cursorColumn()*12, m_mainGridItemGroup->pos().y() + static_cast(m_levelOneProxyWidget->widget())->document()->cursorRow()*10); } void LevelOneScene::updateSelection() { if (!static_cast(m_levelOneProxyWidget->widget())->document()->selectionActive()) { m_selectionRectItem->setVisible(false); return; } m_selectionRectItem->setRect(m_mainGridItemGroup->pos().x() + static_cast(m_levelOneProxyWidget->widget())->document()->selectionLeftColumn()*12, m_mainGridItemGroup->pos().y() + static_cast(m_levelOneProxyWidget->widget())->document()->selectionTopRow()*10, static_cast(m_levelOneProxyWidget->widget())->document()->selectionWidth()*12-1, static_cast(m_levelOneProxyWidget->widget())->document()->selectionHeight()*10-1); m_selectionRectItem->setVisible(true); } void LevelOneScene::setMix(bool mix) { if (mix) { m_fullScreenTopRectItem->setBrush(Qt::transparent); m_fullScreenBottomRectItem->setBrush(Qt::transparent); for (int r=0; r<25; r++) { m_fullRowLeftRectItem[r]->setBrush(Qt::transparent); m_fullRowRightRectItem[r]->setBrush(Qt::transparent); } } else { setFullScreenColour(static_cast(m_levelOneProxyWidget->widget())->pageDecode()->fullScreenQColor()); for (int r=0; r<25; r++) setFullRowColour(r, static_cast(m_levelOneProxyWidget->widget())->pageDecode()->fullRowQColor(r)); } } void LevelOneScene::toggleGrid(bool gridOn) { m_grid = gridOn; m_mainGridItemGroup->setVisible(gridOn); for (int i=0; i<32; i++) if (m_sidePanelGridNeeded[i]) m_sidePanelGridItemGroup[i]->setVisible(gridOn); } void LevelOneScene::hideGUIElements(bool hidden) { if (hidden) { m_cursorRectItem->setVisible(false); m_selectionRectItem->setVisible(false); } else { if (static_cast(m_levelOneProxyWidget->widget())->document()->selectionActive()) m_selectionRectItem->setVisible(true); m_cursorRectItem->setVisible(true); } } // Implements Ctrl+mousewheel zoom bool LevelOneScene::eventFilter(QObject *object, QEvent *event) { Q_UNUSED(object); if (event->type() == QEvent::GraphicsSceneWheel && static_cast(event)->modifiers() == Qt::ControlModifier) { if (static_cast(event)->delta() > 0) emit mouseZoomIn(); else if (static_cast(event)->delta() < 0) emit mouseZoomOut(); event->accept(); return true; } return false; } // Clicking outside the main text widget but still within the scene would // cause keyboard focus loss. // So on every keypress within the scene, wrench the focus back to the widget // if necessary. void LevelOneScene::keyPressEvent(QKeyEvent *keyEvent) { if (focusItem() != m_levelOneProxyWidget) setFocusItem(m_levelOneProxyWidget); QGraphicsScene::keyPressEvent(keyEvent); } void LevelOneScene::keyReleaseEvent(QKeyEvent *keyEvent) { if (focusItem() != m_levelOneProxyWidget) setFocusItem(m_levelOneProxyWidget); QGraphicsScene::keyReleaseEvent(keyEvent); } void LevelOneScene::setFullScreenColour(const QColor &newColor) { if (!static_cast(m_levelOneProxyWidget->widget())->pageRender()->mix()) { m_fullScreenTopRectItem->setBrush(newColor); m_fullScreenBottomRectItem->setBrush(newColor); } } void LevelOneScene::setFullRowColour(int row, const QColor &newColor) { if (!static_cast(m_levelOneProxyWidget->widget())->pageRender()->mix()) { m_fullRowLeftRectItem[row]->setBrush(newColor); m_fullRowRightRectItem[row]->setBrush(newColor); } }