File indexing completed on 2024-03-24 04:05:15

0001 /*
0002     SPDX-FileCopyrightText: 1997 Mathias Mueller <in5y158@public.uni-hamburg.de>
0003     SPDX-FileCopyrightText: 2006 Mauricio Piacentini <mauricio@tabuleiro.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 // own
0009 #include "editor.h"
0010 
0011 // Qt
0012 #include <QAction>
0013 #include <QFileDialog>
0014 #include <QFileInfo>
0015 #include <QGridLayout>
0016 #include <QIcon>
0017 #include <QLabel>
0018 #include <QPainter>
0019 #include <QResizeEvent>
0020 #include <QActionGroup>
0021 
0022 // KF
0023 #include <KActionCollection>
0024 #include <KLocalizedString>
0025 #include <KMessageBox>
0026 #include <KStandardAction>
0027 #include <KToggleAction>
0028 
0029 // KMahjongg
0030 #include "prefs.h"
0031 
0032 Editor::Editor(QWidget * parent)
0033     : QDialog(parent)
0034     , m_mode(EditMode::insert)
0035     , m_borderLeft(0)
0036     , m_borderTop(0)
0037     , m_numTiles(0)
0038     , m_clean(true)
0039     , m_drawFrame(nullptr)
0040     , m_tiles()
0041     , m_theLabel(nullptr)
0042     , m_topToolbar(nullptr)
0043     , m_actionCollection(nullptr)
0044 {
0045     setModal(true);
0046 
0047     QWidget * mainWidget = new QWidget(this);
0048 
0049     QVBoxLayout * mainLayout = new QVBoxLayout(this);
0050     mainLayout->addWidget(mainWidget);
0051 
0052     resize(QSize(800, 400));
0053 
0054     QGridLayout * gridLayout = new QGridLayout(mainWidget);
0055     QVBoxLayout * layout = new QVBoxLayout();
0056 
0057     setupToolbar();
0058     layout->addWidget(m_topToolbar);
0059 
0060     m_drawFrame = new FrameImage(this, QSize(0, 0));
0061     m_drawFrame->setFocusPolicy(Qt::NoFocus);
0062     m_drawFrame->setMouseTracking(true);
0063 
0064     layout->addWidget(m_drawFrame);
0065     gridLayout->addLayout(layout, 0, 0, 1, 1);
0066 
0067     //toolbar will set our minimum height
0068     setMinimumHeight(120);
0069 
0070     // tell the user what we do
0071     setWindowTitle(i18nc("@title:window", "Edit Board Layout"));
0072 
0073     connect(m_drawFrame, &FrameImage::mousePressed, this, &Editor::drawFrameMousePressEvent);
0074     connect(m_drawFrame, &FrameImage::mouseMoved, this, &Editor::drawFrameMouseMovedEvent);
0075 
0076     statusChanged();
0077 
0078     update();
0079 }
0080 
0081 Editor::~Editor()
0082 {
0083 }
0084 
0085 void Editor::updateTileSize(const QSize size)
0086 {
0087     const int width = m_theBoard.getWidth();
0088     const int height = m_theBoard.getHeight();
0089     const QSize tileSize = m_tiles.preferredTileSize(size, width / 2, height / 2);
0090 
0091     m_tiles.reloadTileset(tileSize);
0092     m_borderLeft = (m_drawFrame->size().width() - (width * m_tiles.qWidth())) / 2;
0093     m_borderTop = (m_drawFrame->size().height() - (height * m_tiles.qHeight())) / 2;
0094 }
0095 
0096 void Editor::resizeEvent(QResizeEvent * event)
0097 {
0098     updateTileSize(event->size());
0099 }
0100 
0101 void Editor::setupToolbar()
0102 {
0103     m_topToolbar = new KToolBar(QStringLiteral("editToolBar"), this);
0104     m_topToolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
0105 
0106     m_actionCollection = new KActionCollection(this);
0107 
0108     // new game
0109     QAction * newBoard = m_actionCollection->addAction(QStringLiteral("new_board"));
0110     newBoard->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
0111     newBoard->setText(i18n("New board"));
0112     connect(newBoard, &QAction::triggered, this, &Editor::newBoard);
0113     m_topToolbar->addAction(newBoard);
0114 
0115     // open game
0116     QAction * openBoard = m_actionCollection->addAction(QStringLiteral("open_board"));
0117     openBoard->setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
0118     openBoard->setText(i18n("Open board"));
0119     connect(openBoard, &QAction::triggered, this, &Editor::loadBoard);
0120     m_topToolbar->addAction(openBoard);
0121 
0122     // save game
0123     QAction * saveBoard = m_actionCollection->addAction(QStringLiteral("save_board"));
0124     saveBoard->setIcon(QIcon::fromTheme(QStringLiteral("document-save")));
0125     saveBoard->setText(i18n("Save board"));
0126     connect(saveBoard, &QAction::triggered, this, &Editor::saveBoard);
0127     m_topToolbar->addAction(saveBoard);
0128 
0129     m_topToolbar->addSeparator();
0130 
0131 #ifdef FUTURE_OPTIONS
0132     // Select
0133     QAction * select = actionCollection->addAction(QLatin1String("select"));
0134     select->setIcon(QIcon::fromTheme(QLatin1String("rectangle_select")));
0135     select->setText(i18n("Select"));
0136     topToolbar->addAction(select);
0137 
0138     QAction * cut = actionCollection->addAction(QLatin1String("edit_cut"));
0139     cut->setIcon(QIcon::fromTheme(QLatin1String("edit-cut")));
0140     cut->setText(i18n("Cut"));
0141     topToolbar->addAction(cut);
0142 
0143     QAction * copy = actionCollection->addAction(QLatin1String("edit_copy"));
0144     copy->setIcon(QIcon::fromTheme(QLatin1String("edit-copy")));
0145     copy->setText(i18n("Copy"));
0146     topToolbar->addAction(copy);
0147 
0148     QAction * paste = actionCollection->addAction(QLatin1String("edit_paste"));
0149     paste->setIcon(QIcon::fromTheme(QLatin1String("edit-paste")));
0150     paste->setText(i18n("Paste"));
0151     topToolbar->addAction(paste);
0152 
0153     topToolbar->addSeparator();
0154 
0155     QAction * moveTiles = actionCollection->addAction(QLatin1String("move_tiles"));
0156     moveTiles->setIcon(QIcon::fromTheme(QLatin1String("move")));
0157     moveTiles->setText(i18n("Move tiles"));
0158     topToolbar->addAction(moveTiles);
0159 #endif
0160 
0161     KToggleAction * addTiles = new KToggleAction(QIcon::fromTheme(QStringLiteral("draw-freehand")), i18n("Add tiles"), this);
0162     m_actionCollection->addAction(QStringLiteral("add_tiles"), addTiles);
0163     m_topToolbar->addAction(addTiles);
0164     KToggleAction * delTiles = new KToggleAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("Remove tiles"), this);
0165     m_actionCollection->addAction(QStringLiteral("del_tiles"), delTiles);
0166     m_topToolbar->addAction(delTiles);
0167 
0168     QActionGroup * radioGrp = new QActionGroup(this);
0169     radioGrp->setExclusive(true);
0170     radioGrp->addAction(addTiles);
0171     addTiles->setChecked(true);
0172 
0173 #ifdef FUTURE_OPTIONS
0174     radioGrp->addAction(moveTiles);
0175 #endif
0176 
0177     radioGrp->addAction(delTiles);
0178     connect(radioGrp, &QActionGroup::triggered, this, &Editor::slotModeChanged);
0179 
0180     // board shift
0181 
0182     m_topToolbar->addSeparator();
0183 
0184     // NOTE: maybe join shiftActions in QActionGroup and create one slot(QAction*) instead of 4 slots? ;)
0185     // Does this makes sense? dimsuz
0186     QAction * shiftLeft = m_actionCollection->addAction(QStringLiteral("shift_left"));
0187     shiftLeft->setIcon(QIcon::fromTheme(QStringLiteral("go-previous")));
0188     shiftLeft->setText(i18n("Shift left"));
0189     connect(shiftLeft, &QAction::triggered, this, &Editor::slotShiftLeft);
0190     m_topToolbar->addAction(shiftLeft);
0191 
0192     QAction * shiftUp = m_actionCollection->addAction(QStringLiteral("shift_up"));
0193     shiftUp->setIcon(QIcon::fromTheme(QStringLiteral("go-up")));
0194     shiftUp->setText(i18n("Shift up"));
0195     connect(shiftUp, &QAction::triggered, this, &Editor::slotShiftUp);
0196     m_topToolbar->addAction(shiftUp);
0197 
0198     QAction * shiftDown = m_actionCollection->addAction(QStringLiteral("shift_down"));
0199     shiftDown->setIcon(QIcon::fromTheme(QStringLiteral("go-down")));
0200     shiftDown->setText(i18n("Shift down"));
0201     connect(shiftDown, &QAction::triggered, this, &Editor::slotShiftDown);
0202     m_topToolbar->addAction(shiftDown);
0203 
0204     QAction * shiftRight = m_actionCollection->addAction(QStringLiteral("shift_right"));
0205     shiftRight->setIcon(QIcon::fromTheme(QStringLiteral("go-next")));
0206     shiftRight->setText(i18n("Shift right"));
0207     connect(shiftRight, &QAction::triggered, this, &Editor::slotShiftRight);
0208     m_topToolbar->addAction(shiftRight);
0209 
0210     m_topToolbar->addSeparator();
0211     QAction * quit = m_actionCollection->addAction(KStandardAction::Quit, QStringLiteral("quit"));
0212     connect(quit, &QAction::triggered, this, &Editor::close);
0213     m_topToolbar->addAction(quit);
0214 
0215     // status in the toolbar for now (ick)
0216     QWidget * hbox = new QWidget(m_topToolbar);
0217     QHBoxLayout * layout = new QHBoxLayout(hbox);
0218     layout->setContentsMargins(0, 0, 0, 0);
0219     layout->setSpacing(0);
0220     layout->addStretch();
0221 
0222     m_theLabel = new QLabel(statusText(), hbox);
0223     layout->addWidget(m_theLabel);
0224     m_topToolbar->addWidget(hbox);
0225 
0226     m_topToolbar->adjustSize();
0227     setMinimumWidth(m_topToolbar->width());
0228 }
0229 
0230 void Editor::statusChanged() const
0231 {
0232     const bool canSave = ((m_numTiles != 0) && ((m_numTiles & 1) == 0));
0233     m_theLabel->setText(statusText());
0234     m_actionCollection->action(QStringLiteral("save_board"))->setEnabled(canSave);
0235 }
0236 
0237 void Editor::slotShiftLeft()
0238 {
0239     m_theBoard.shiftLeft();
0240     update();
0241 }
0242 
0243 void Editor::slotShiftRight()
0244 {
0245     m_theBoard.shiftRight();
0246     update();
0247 }
0248 
0249 void Editor::slotShiftUp()
0250 {
0251     m_theBoard.shiftUp();
0252     update();
0253 }
0254 
0255 void Editor::slotShiftDown()
0256 {
0257     m_theBoard.shiftDown();
0258     update();
0259 }
0260 
0261 void Editor::slotModeChanged(QAction * act)
0262 {
0263     if (act == m_actionCollection->action(QStringLiteral("move_tiles"))) {
0264         m_mode = EditMode::move;
0265     } else if (act == m_actionCollection->action(QStringLiteral("del_tiles"))) {
0266         m_mode = EditMode::remove;
0267     } else if (act == m_actionCollection->action(QStringLiteral("add_tiles"))) {
0268         m_mode = EditMode::insert;
0269     }
0270 }
0271 
0272 QString Editor::statusText() const
0273 {
0274     int x = m_curPos.x;
0275     int y = m_curPos.y;
0276     int z = m_curPos.z;
0277 
0278     if (z == 100) {
0279         z = 0;
0280     } else {
0281         z = z + 1;
0282     }
0283 
0284     if (x >= m_theBoard.getWidth() || x < 0 || y >= m_theBoard.getHeight() || y < 0) {
0285         x = y = z = 0;
0286     }
0287 
0288     return i18n("Tiles: %1 Pos: %2,%3,%4", m_numTiles, x, y, z);
0289 }
0290 
0291 void Editor::loadBoard()
0292 {
0293     if (!testSave()) {
0294         return;
0295     }
0296 
0297     const QString filename = QFileDialog::getOpenFileName(this, i18n("Open Board Layout"), QString(),
0298                                                           i18n("Board Layout (*.layout);;All Files (*)"));
0299 
0300     if (filename.isEmpty()) {
0301         return;
0302     }
0303 
0304     m_theBoard.loadBoardLayout(filename);
0305     update();
0306 }
0307 
0308 void Editor::newBoard()
0309 {
0310     // Clear out the contents of the board. Repaint the screen
0311     // set values to their defaults.
0312 
0313     if (!testSave()) {
0314         return;
0315     }
0316 
0317     m_theBoard.clearBoardLayout();
0318 
0319     m_clean = true;
0320     m_numTiles = 0;
0321 
0322     statusChanged();
0323     update();
0324 }
0325 
0326 bool Editor::saveBoard()
0327 {
0328     if (!((m_numTiles != 0) && ((m_numTiles & 1) == 0))) {
0329         KMessageBox::error(this, i18n("You can only save with a even number of tiles."));
0330 
0331         return false;
0332     }
0333 
0334     // get a save file name
0335     const QString filename = QFileDialog::getSaveFileName(this, i18n("Save Board Layout"), QString(),
0336                                                           i18n("Board Layout (*.layout);;All Files (*)"));
0337 
0338     if (filename.isEmpty()) {
0339         return false;
0340     }
0341 
0342     const QFileInfo f(filename);
0343     if (f.exists()) {
0344         // if it already exists, query the user for replacement
0345         int res = KMessageBox::warningContinueCancel(this,
0346                                                      i18n("A file with that name already exists. Do you wish to overwrite it?"),
0347                                                      i18n("Save Board Layout"), KStandardGuiItem::save());
0348 
0349         if (res != KMessageBox::Continue) {
0350             return false;
0351         }
0352     }
0353 
0354     bool result = m_theBoard.saveBoardLayout(filename);
0355 
0356     if (result == true) {
0357         m_clean = true;
0358 
0359         return true;
0360     } else {
0361         return false;
0362     }
0363 }
0364 
0365 bool Editor::testSave()
0366 {
0367     // test if a save is required and return true if the app is to continue
0368     // false if cancel is selected. (if ok then call out to save the board
0369 
0370     if (m_clean) {
0371         return true;
0372     }
0373 
0374     const int res = KMessageBox::warningTwoActionsCancel(this,
0375                                                     i18n("The board has been modified. Would you like to save the changes?"),
0376                                                     QString(), KStandardGuiItem::save(), KStandardGuiItem::dontSave());
0377 
0378     if (res == KMessageBox::PrimaryAction) {
0379         // yes to save
0380         if (saveBoard()) {
0381             return true;
0382         } else {
0383             KMessageBox::error(this, i18n("Save failed. Aborting operation."));
0384 
0385             return false;
0386         }
0387     } else {
0388         return (res != KMessageBox::Cancel);
0389     }
0390     return true;
0391 }
0392 
0393 void Editor::paintEvent(QPaintEvent *)
0394 {
0395     // The main paint event, draw in the grid and blit in
0396     // the tiles as specified by the layout.
0397 
0398     // first we layer on a background grid
0399     QPixmap buff;
0400     QPixmap * dest = m_drawFrame->getPreviewPixmap();
0401     buff = QPixmap(dest->width(), dest->height());
0402     drawBackground(&buff);
0403     drawTiles(&buff);
0404     QPainter p(dest);
0405     p.drawPixmap(0, 0, buff);
0406     p.end();
0407 
0408     m_drawFrame->update();
0409 }
0410 
0411 void Editor::drawBackground(QPixmap * pixmap) const
0412 {
0413     const int width = m_theBoard.getWidth();
0414     const int height = m_theBoard.getHeight();
0415     QPainter p(pixmap);
0416 
0417     // blast in a white background
0418     p.fillRect(0, 0, pixmap->width(), pixmap->height(), Qt::white);
0419 
0420     // now put in a grid of tile quarter width squares
0421     for (int y = 0; y <= height; ++y) {
0422         int nextY = m_borderTop + (y * m_tiles.qHeight());
0423         p.drawLine(m_borderLeft, nextY, m_borderLeft + (width * m_tiles.qWidth()), nextY);
0424     }
0425 
0426     for (int x = 0; x <= width; ++x) {
0427         int nextX = m_borderLeft + (x * m_tiles.qWidth());
0428         p.drawLine(nextX, m_borderTop, nextX, m_borderTop + (height * m_tiles.qHeight()));
0429     }
0430 }
0431 
0432 void Editor::drawTiles(QPixmap * dest)
0433 {
0434     QPainter p(dest);
0435 
0436     const int width = m_theBoard.getWidth();
0437     const int height = m_theBoard.getHeight();
0438     const int depth = m_theBoard.getDepth();
0439     const int shadowX = m_tiles.width() - m_tiles.qWidth() * 2 - m_tiles.levelOffsetX();
0440     const int shadowY = m_tiles.height() - m_tiles.qHeight() * 2 - m_tiles.levelOffsetY();
0441     short tile = 0;
0442 
0443     int xOffset = -shadowX;
0444     int yOffset = -m_tiles.levelOffsetY();
0445 
0446     // we iterate over the depth stacking order. Each successive level is
0447     // drawn one indent up and to the right. The indent is the width
0448     // of the 3d relief on the tile left (tile shadow width)
0449     for (int z = 0; z < depth; ++z) {
0450         // we draw down the board so the tile below over rights our border
0451         for (int y = 0; y < height; ++y) {
0452             // drawing right to left to prevent border overwrite
0453             for (int x = width - 1; x >= 0; --x) {
0454                 int sx = x * m_tiles.qWidth() + xOffset + m_borderLeft;
0455                 int sy = y * m_tiles.qHeight() + yOffset + m_borderTop;
0456 
0457                 if (m_theBoard.getBoardData(z, y, x) != '1') {
0458                     continue;
0459                 }
0460 
0461                 QPixmap t;
0462                 tile = (z * depth) + (y * height) + (x * width);
0463                 t = m_tiles.unselectedTile(0);
0464 
0465                 // Only one complication. Since we render top to bottom , left
0466                 // to right situations arise where...:
0467                 // there exists a tile one q height above and to the left
0468                 // in this situation we would draw our top left border over it
0469                 // we simply split the tile draw so the top half is drawn
0470                 // minus border
0471                 if ((x > 1) && (y > 0) && m_theBoard.getBoardData(z, y - 1, x - 2) == '1') {
0472                     p.drawPixmap(sx, sy, t, 0, 0, t.width(), t.height());
0473 
0474                     p.drawPixmap(sx - m_tiles.qWidth() + shadowX + m_tiles.levelOffsetX(), sy, t,
0475                                  t.width() - m_tiles.qWidth(),
0476                                  t.height() - m_tiles.qHeight() - m_tiles.levelOffsetX() - shadowY,
0477                                  m_tiles.qWidth(), m_tiles.qHeight() + m_tiles.levelOffsetX());
0478                 } else {
0479                     p.drawPixmap(sx, sy, t, 0, 0, t.width(), t.height());
0480                 }
0481 
0482                 ++tile;
0483                 tile = tile % 143;
0484             }
0485         }
0486 
0487         xOffset += m_tiles.levelOffsetX();
0488         yOffset -= m_tiles.levelOffsetY();
0489     }
0490 }
0491 
0492 void Editor::transformPointToPosition(const QPoint & point, POSITION & mouseClickPos, bool align) const
0493 {
0494     // convert mouse position on screen to a tile z y x coord
0495     // different to the one in kmahjongg.cpp since if we hit ground
0496     // we return a result too.
0497 
0498     short z = 0;
0499     short y = 0;
0500     short x = 0;
0501     mouseClickPos.z = 100;
0502 
0503     // iterate over z coordinate from top to bottom
0504     for (z = m_theBoard.getDepth() - 1; z >= 0; --z) {
0505         // calculate mouse coordinates --> position in game board
0506         // the factor -theTiles.width()/2 must keep track with the
0507         // offset for blitting in the print event (FIX ME)
0508         x = ((point.x() - m_borderLeft) - (z + 1) * m_tiles.levelOffsetX()) / m_tiles.qWidth();
0509         y = ((point.y() - m_borderTop) + z * m_tiles.levelOffsetX()) / m_tiles.qHeight();
0510 
0511         // skip when position is illegal
0512         if (x < 0 || x >= m_theBoard.getWidth() || y < 0 || y >= m_theBoard.getHeight()) {
0513             continue;
0514         }
0515 
0516         switch (m_theBoard.getBoardData(z, y, x)) {
0517             case static_cast<UCHAR>('3'):
0518                 if (align) {
0519                     --x;
0520                     --y;
0521                 }
0522 
0523                 break;
0524 
0525             case static_cast<UCHAR>('2'):
0526                 if (align) {
0527                     --x;
0528                 }
0529 
0530                 break;
0531 
0532             case static_cast<UCHAR>('4'):
0533                 if (align) {
0534                     --y;
0535                 }
0536 
0537                 break;
0538 
0539             case static_cast<UCHAR>('1'):
0540                 break;
0541 
0542             default:
0543                 continue;
0544         }
0545 
0546         // if gameboard is empty, skip
0547         if (!m_theBoard.getBoardData(z, y, x)) {
0548             continue;
0549         }
0550 
0551         // here, position is legal
0552         mouseClickPos.z = z;
0553         mouseClickPos.y = y;
0554         mouseClickPos.x = x;
0555         mouseClickPos.f = m_theBoard.getBoardData(z, y, x);
0556 
0557         break;
0558     }
0559 
0560     if (mouseClickPos.z == 100) {
0561         mouseClickPos.x = x;
0562         mouseClickPos.y = y;
0563         mouseClickPos.f = 0;
0564     }
0565 }
0566 
0567 void Editor::drawFrameMousePressEvent(QMouseEvent * e)
0568 {
0569     // we swallow the draw frames mouse clicks and process here
0570 
0571     POSITION mousePos;
0572     transformPointToPosition(e->pos(), mousePos, (m_mode == EditMode::remove));
0573 
0574     switch (m_mode) {
0575         case EditMode::remove:
0576             if (!m_theBoard.tileAbove(mousePos) && mousePos.z < m_theBoard.getDepth() && m_theBoard.isTileAt(mousePos)) {
0577                 m_theBoard.deleteTile(mousePos);
0578                 --m_numTiles;
0579                 statusChanged();
0580                 drawFrameMouseMovedEvent(e);
0581                 update();
0582             }
0583 
0584             break;
0585         case EditMode::insert: {
0586             POSITION n = mousePos;
0587 
0588             if (n.z == 100) {
0589                 n.z = 0;
0590             } else {
0591                 n.z += 1;
0592             }
0593 
0594             if (canInsert(n)) {
0595                 m_theBoard.insertTile(n);
0596                 m_clean = false;
0597                 ++m_numTiles;
0598                 statusChanged();
0599                 update();
0600             }
0601 
0602             break;
0603         }
0604         default:
0605             break;
0606     }
0607 }
0608 
0609 void Editor::drawCursor(POSITION & p, bool visible)
0610 {
0611     int x = m_borderLeft + (p.z * m_tiles.levelOffsetX()) + (p.x * m_tiles.qWidth());
0612     const int y = m_borderTop - ((p.z + 1) * m_tiles.levelOffsetY()) + (p.y * m_tiles.qHeight());
0613     const int w = (m_tiles.qWidth() * 2) + m_tiles.levelOffsetX();
0614     const int h = (m_tiles.qHeight() * 2) + m_tiles.levelOffsetY();
0615 
0616     if (p.z == 100 || !visible) {
0617         x = -1;
0618     }
0619 
0620     m_drawFrame->setRect(x, y, w, h, m_tiles.levelOffsetX(), static_cast<int>(m_mode) - static_cast<int>(EditMode::remove));
0621     m_drawFrame->update();
0622 }
0623 
0624 void Editor::drawFrameMouseMovedEvent(QMouseEvent * e)
0625 {
0626     // we swallow the draw frames mouse moves and process here
0627 
0628     POSITION mousePos;
0629     transformPointToPosition(e->pos(), mousePos, (m_mode == EditMode::remove));
0630 
0631     if ((mousePos.x == m_curPos.x) && (mousePos.y == m_curPos.y) && (mousePos.z == m_curPos.z)) {
0632         return;
0633     }
0634 
0635     m_curPos = mousePos;
0636 
0637     statusChanged();
0638 
0639     switch (m_mode) {
0640         case EditMode::insert: {
0641             POSITION next;
0642             next = m_curPos;
0643 
0644             if (next.z == 100) {
0645                 next.z = 0;
0646             } else {
0647                 next.z += 1;
0648             }
0649 
0650             drawCursor(next, canInsert(next));
0651 
0652             break;
0653         }
0654         case EditMode::remove:
0655             drawCursor(m_curPos, 1);
0656 
0657             break;
0658         case EditMode::move:
0659             break;
0660     }
0661 }
0662 
0663 bool Editor::canInsert(POSITION & p) const
0664 {
0665     // can we inser a tile here. We can iff
0666     // there are tiles in all positions below us (or we are a ground level)
0667     // there are no tiles intersecting with us on this level
0668 
0669     if (p.z >= m_theBoard.getDepth()) {
0670         return false;
0671     }
0672 
0673     if (p.y > m_theBoard.getHeight() - 2) {
0674         return false;
0675     }
0676 
0677     if (p.x > m_theBoard.getWidth() - 2) {
0678         return false;
0679     }
0680 
0681     POSITION n = p;
0682 
0683     if (p.z != 0) {
0684         n.z -= 1;
0685         if (!m_theBoard.allFilled(n)) {
0686             return false;
0687         }
0688     }
0689 
0690     return !m_theBoard.anyFilled(p);
0691 }
0692 
0693 void Editor::closeEvent(QCloseEvent * e)
0694 {
0695     if (testSave()) {
0696         m_theBoard.clearBoardLayout();
0697         m_clean = true;
0698         m_numTiles = 0;
0699         statusChanged();
0700         update();
0701 
0702         // Save the window geometry.
0703         Prefs::setEditorGeometry(geometry());
0704         Prefs::self()->save();
0705 
0706         e->accept();
0707     } else {
0708         e->ignore();
0709     }
0710 }
0711 
0712 void Editor::setTilesetFromSettings()
0713 {
0714     const QString tileset(Prefs::tileSet());
0715 
0716     // Exit if the tileset is already set.
0717     if (tileset == m_tileset) {
0718         return;
0719     }
0720 
0721     // Try to load the new tileset.
0722     if (!m_tiles.loadTileset(tileset)) {
0723         // Try to load the old one.
0724         if (!m_tiles.loadTileset(m_tileset)) {
0725             m_tiles.loadDefault();
0726         }
0727     } else {
0728         // If loading the new tileset was ok, set the new tileset name.
0729         m_tileset = tileset;
0730     }
0731 
0732     // Must be called to load the graphics and its information.
0733     m_tiles.loadGraphics();
0734 
0735     updateTileSize(size());
0736 }
0737 
0738 #include "moc_editor.cpp"