1/* Copyright (C) 1997 Mathias Mueller <in5y158@public.uni-hamburg.de>
2 * Copyright (C) 2006 Mauricio Piacentini <mauricio@tabuleiro.com>
3 *
4 * Kmahjongg is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
17
18#include "Editor.h"
19#include "prefs.h"
20
21#include <QLabel>
22#include <qevent.h>
23#include <qpainter.h>
24#include <QHBoxLayout>
25#include <QGridLayout>
26
27#include <kmessagebox.h>
28#include <kcomponentdata.h>
29#include <klocale.h>
30#include <kstandarddirs.h>
31#include <kaction.h>
32#include <kactioncollection.h>
33#include <ktoggleaction.h>
34#include <kstandardaction.h>
35#include <kicon.h>
36
37
38Editor::Editor(QWidget *parent)
39 : KDialog( parent ),
40 tiles()
41{
42 setModal(true);
43 clean = true;
44 numTiles = 0;
45 mode = insert;
46
47 QWidget *mainWidget = new QWidget(this);
48 setMainWidget(mainWidget);
49
50 QGridLayout *gridLayout = new QGridLayout(mainWidget);
51 QVBoxLayout *layout = new QVBoxLayout();
52
53 // setup the tool bar
54 setupToolbar();
55 layout->addWidget(topToolbar);
56
57 drawFrame = new FrameImage(this, QSize(0, 0));
58 drawFrame->setFocusPolicy(Qt::NoFocus);
59 drawFrame->setMouseTracking(true);
60
61 layout->addWidget(drawFrame);
62 gridLayout->addLayout(layout, 0, 0, 1, 1);
63
64 //toolbar will set our minimum height
65 setMinimumHeight(120);
66
67 // tell the user what we do
68 setCaption(i18n("Edit Board Layout"));
69
70 connect(drawFrame, SIGNAL(mousePressed(QMouseEvent*)), this, SLOT(
71 drawFrameMousePressEvent(QMouseEvent*)));
72 connect(drawFrame, SIGNAL(mouseMoved(QMouseEvent*)), this, SLOT(
73 drawFrameMouseMovedEvent(QMouseEvent*)));
74
75 statusChanged();
76
77 setButtons(KDialog::None);
78
79 update();
80}
81
82Editor::~Editor()
83{
84}
85
86void Editor::setTileset(const QString tileset)
87{
88 // Exit if the tileset is already set.
89 if (tileset == mTileset) {
90 return;
91 }
92
93 // Try to load the new tileset.
94 if (!tiles.loadTileset(tileset)) {
95 // Try to load the old one.
96 if (!tiles.loadTileset(mTileset)) {
97 tiles.loadDefault();
98 }
99 } else {
100 // If loading the new tileset was ok, set the new tileset name.
101 mTileset = tileset;
102 }
103
104 // Must be called to load the graphics and its informations.
105 tiles.loadGraphics();
106
107 updateTileSize(size());
108}
109
110const QString Editor::getTileset() const
111{
112 return mTileset;
113}
114
115void Editor::updateTileSize(const QSize size)
116{
117 QSize tileSize = tiles.preferredTileSize(size, theBoard.m_width / 2, theBoard.m_height / 2);
118 tiles.reloadTileset(tileSize);
119
120 borderLeft = (drawFrame->size().width() - (theBoard.m_width * tiles.qWidth())) / 2;
121 borderTop = (drawFrame->size().height() - (theBoard.m_height * tiles.qHeight())) / 2;
122}
123
124void Editor::resizeEvent(QResizeEvent *event)
125{
126 updateTileSize(event->size());
127}
128
129void Editor::setupToolbar()
130{
131 topToolbar = new KToolBar(this, "editToolBar");
132 topToolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
133
134 actionCollection = new KActionCollection(this);
135
136 // new game
137 QAction *newBoard = actionCollection->addAction(QLatin1String("new_board"));
138 newBoard->setIcon(KIcon(QLatin1String("document-new")));
139 newBoard->setText(i18n("New board"));
140 connect(newBoard, SIGNAL(triggered(bool)), SLOT(newBoard()));
141 topToolbar->addAction(newBoard);
142
143 // open game
144 QAction *openBoard = actionCollection->addAction(QLatin1String("open_board"));
145 openBoard->setIcon(KIcon(QLatin1String("document-open")));
146 openBoard->setText(i18n("Open board"));
147 connect(openBoard, SIGNAL(triggered(bool)), SLOT(loadBoard()));
148 topToolbar->addAction(openBoard);
149
150 // save game
151 QAction *saveBoard = actionCollection->addAction(QLatin1String("save_board"));
152 saveBoard->setIcon(KIcon(QLatin1String("document-save")));
153 saveBoard->setText(i18n("Save board"));
154 connect(saveBoard, SIGNAL(triggered(bool)), SLOT(saveBoard()));
155 topToolbar->addAction(saveBoard);
156 // NOTE dimsuz: how to port this? is it even needed?
157 //topToolbar->setButtonIconSet(ID_TOOL_SAVE,loader->loadIconSet("document-save", KIconLoader::Toolbar));
158
159 topToolbar->addSeparator();
160
161
162#ifdef FUTURE_OPTIONS
163
164
165 // Select
166 QAction *select = actionCollection->addAction(QLatin1String("select"));
167 select->setIcon(KIcon(QLatin1String("rectangle_select")));
168 select->setText(i18n("Select"));
169 topToolbar->addAction(select);
170
171 // NOTE: use kstandarddactions?
172 QAction *cut = actionCollection->addAction(QLatin1String("edit_cut"));
173 cut->setIcon(KIcon(QLatin1String("edit-cut")));
174 cut->setText(i18n("Cut"));
175 topToolbar->addAction(cut);
176
177 QAction *copy = actionCollection->addAction(QLatin1String("edit_copy"));
178 copy->setIcon(KIcon(QLatin1String("edit-copy")));
179 copy->setText(i18n("Copy"));
180 topToolbar->addAction(copy);
181
182 QAction *paste = actionCollection->addAction(QLatin1String("edit_paste"));
183 paste->setIcon(KIcon(QLatin1String("edit-paste")));
184 paste->setText(i18n("Paste"));
185 topToolbar->addAction(paste);
186
187 topToolbar->addSeparator();
188
189 QAction *moveTiles = actionCollection->addAction(QLatin1String("move_tiles"));
190 moveTiles->setIcon(KIcon(QLatin1String("move")));
191 moveTiles->setText(i18n("Move tiles"));
192 topToolbar->addAction(moveTiles);
193
194
195#endif
196
197
198 KToggleAction *addTiles = new KToggleAction(KIcon(QLatin1String("draw-freehand")), i18n("Add ti"
199 "les"), this);
200 actionCollection->addAction(QLatin1String("add_tiles"), addTiles);
201 topToolbar->addAction(addTiles);
202 KToggleAction *delTiles = new KToggleAction(KIcon(QLatin1String("edit-delete")), i18n("Remove t"
203 "iles" ), this);
204 actionCollection->addAction(QLatin1String("del_tiles"), delTiles);
205 topToolbar->addAction(delTiles);
206
207 QActionGroup *radioGrp = new QActionGroup(this);
208 radioGrp->setExclusive(true);
209 radioGrp->addAction(addTiles);
210 addTiles->setChecked(true);
211
212
213#ifdef FUTURE_OPTIONS
214
215
216 radioGrp->addAction(moveTiles);
217
218
219#endif
220
221
222 radioGrp->addAction(delTiles);
223 connect(radioGrp, SIGNAL(triggered(QAction*)), SLOT(slotModeChanged(QAction*)));
224
225 // board shift
226
227 topToolbar->addSeparator();
228
229 // NOTE: maybe join shiftActions in QActionGroup and create one slot(QAction*) instead of 4 slots? ;)
230 // Does this makes sense? dimsuz
231 QAction *shiftLeft = actionCollection->addAction(QLatin1String("shift_left"));
232 shiftLeft->setIcon(KIcon(QLatin1String("go-previous")));
233 shiftLeft->setText(i18n("Shift left"));
234 connect(shiftLeft, SIGNAL(triggered(bool)), SLOT(slotShiftLeft()));
235 topToolbar->addAction(shiftLeft);
236
237 QAction *shiftUp = actionCollection->addAction(QLatin1String("shift_up"));
238 shiftUp->setIcon(KIcon(QLatin1String("go-up")));
239 shiftUp->setText(i18n("Shift up"));
240 connect(shiftUp, SIGNAL(triggered(bool)), SLOT(slotShiftUp()));
241 topToolbar->addAction(shiftUp);
242
243 QAction *shiftDown = actionCollection->addAction(QLatin1String("shift_down"));
244 shiftDown->setIcon(KIcon(QLatin1String("go-down")));
245 shiftDown->setText(i18n("Shift down"));
246 connect(shiftDown, SIGNAL(triggered(bool)), SLOT(slotShiftDown()));
247 topToolbar->addAction(shiftDown);
248
249 QAction *shiftRight = actionCollection->addAction(QLatin1String("shift_right"));
250 shiftRight->setIcon(KIcon(QLatin1String("go-next")));
251 shiftRight->setText(i18n("Shift right"));
252 connect(shiftRight, SIGNAL(triggered(bool)), SLOT(slotShiftRight()));
253 topToolbar->addAction(shiftRight);
254
255 topToolbar->addSeparator();
256 QAction *quit = actionCollection->addAction(KStandardAction::Quit, QLatin1String("quit"), this,
257 SLOT(close()));
258 topToolbar->addAction(quit);
259
260 // status in the toolbar for now (ick)
261
262 QWidget *hbox = new QWidget(topToolbar);
263 QHBoxLayout *layout = new QHBoxLayout(hbox);
264 layout->setMargin(0);
265 layout->setSpacing(0);
266 layout->addStretch();
267
268 theLabel = new QLabel(statusText(), hbox);
269 layout->addWidget(theLabel);
270 topToolbar->addWidget(hbox);
271
272 topToolbar->adjustSize();
273 setMinimumWidth(topToolbar->width());
274}
275
276void Editor::statusChanged()
277{
278 bool canSave = ((numTiles != 0) && ((numTiles & 1) == 0));
279 theLabel->setText(statusText());
280 actionCollection->action("save_board")->setEnabled(canSave);
281}
282
283void Editor::slotShiftLeft()
284{
285 theBoard.shiftLeft();
286 update();
287}
288
289void Editor::slotShiftRight()
290{
291 theBoard.shiftRight();
292 update();
293}
294
295void Editor::slotShiftUp()
296{
297 theBoard.shiftUp();
298 update();
299}
300
301void Editor::slotShiftDown()
302{
303 theBoard.shiftDown();
304 update();
305}
306
307void Editor::slotModeChanged(QAction *act)
308{
309 if (act == actionCollection->action("move_tiles")) {
310 mode = move;
311 } else if (act == actionCollection->action("del_tiles")) {
312 mode = remove;
313 } else if (act == actionCollection->action("add_tiles")) {
314 mode = insert;
315 }
316}
317
318QString Editor::statusText()
319{
320 QString buf;
321
322 int x = currPos.x;
323 int y = currPos.y;
324 int z = currPos.e;
325
326 if (z == 100) {
327 z = 0;
328 } else {
329 z = z + 1;
330 }
331
332 if (x >= theBoard.m_width || x < 0 || y >= theBoard.m_height || y < 0) {
333 x = y = z = 0;
334 }
335
336 buf = i18n("Tiles: %1 Pos: %2,%3,%4", numTiles, x, y, z);
337
338 return buf;
339}
340
341
342void Editor::loadBoard()
343{
344 if (!testSave()) {
345 return;
346 }
347
348 KUrl url = KFileDialog::getOpenUrl(KUrl(), i18n("*.layout|Board Layout (*.layout)\n*|All File"
349 "s"), this, i18n("Open Board Layout"));
350
351 if (url.isEmpty()) {
352 return;
353 }
354
355 theBoard.loadBoardLayout(url.path());
356 update();
357}
358
359void Editor::newBoard()
360{
361 // Clear out the contents of the board. Repaint the screen
362 // set values to their defaults.
363
364 if (!testSave()) {
365 return;
366 }
367
368 theBoard.clearBoardLayout();
369
370 clean = true;
371 numTiles = 0;
372
373 statusChanged();
374 update();
375}
376
377bool Editor::saveBoard()
378{
379 if (!((numTiles != 0) && ((numTiles & 1) == 0))) {
380 KMessageBox::sorry(this, i18n( "You can only save with a even number of tiles."));
381
382 return false;
383 }
384
385 // get a save file name
386 KUrl url = KFileDialog::getSaveUrl(KUrl(), i18n("*.layout|Board Layout (*.layout)\n*|All File"
387 "s"), this, i18n("Save Board Layout"));
388
389 if (url.isEmpty()) {
390 return false;
391 }
392
393 if (!url.isLocalFile()) {
394 KMessageBox::sorry(this, i18n("Only saving to local files currently supported."));
395
396 return false;
397 }
398
399 QFileInfo f(url.path());
400 if (f.exists()) {
401 // if it already exists, querie the user for replacement
402 int res = KMessageBox::warningContinueCancel(this, i18n("A file with that name already exis"
403 "ts. Do you wish to overwrite it?"), i18n("Save Board Layout" ),
404 KStandardGuiItem::save());
405
406 if (res != KMessageBox::Continue) {
407 return false;
408 }
409 }
410
411 bool result = theBoard.saveBoardLayout(url.path());
412
413 if (result == true) {
414 clean = true;
415
416 return true;
417 } else {
418 return false;
419 }
420}
421
422bool Editor::testSave()
423{
424 // test if a save is required and return true if the app is to continue
425 // false if cancel is selected. (if ok then call out to save the board
426
427 if (clean) {
428 return true;
429 }
430
431 int res;
432 res = KMessageBox::warningYesNoCancel(this, i18n("The board has been modified. Would you like t"
433 "o save the changes?"), QString(), KStandardGuiItem::save(),KStandardGuiItem::dontSave());
434
435 if (res == KMessageBox::Yes) {
436 // yes to save
437 if (saveBoard()) {
438 return true;
439 } else {
440 KMessageBox::sorry(this, i18n("Save failed. Aborting operation."));
441
442 return false;
443 }
444 } else {
445 return (res != KMessageBox::Cancel);
446 }
447
448 return true;
449}
450
451void Editor::paintEvent(QPaintEvent*)
452{
453 // The main paint event, draw in the grid and blit in
454 // the tiles as specified by the layout.
455
456 // first we layer on a background grid
457 QPixmap buff;
458 QPixmap *dest=drawFrame->getPreviewPixmap();
459 buff = QPixmap(dest->width(), dest->height());
460 drawBackground(&buff);
461 drawTiles(&buff);
462 QPainter p(dest);
463 p.drawPixmap(0, 0, buff);
464 p.end();
465
466 drawFrame->update();
467}
468
469void Editor::drawBackground(QPixmap *pixmap)
470{
471 QPainter p(pixmap);
472
473 // blast in a white background
474 p.fillRect(0, 0, pixmap->width(), pixmap->height(), Qt::white);
475
476 // now put in a grid of tile quater width squares
477 int sy = tiles.qHeight();
478 int sx = tiles.qWidth();
479
480 for (int y = 0; y <= theBoard.m_height; y++) {
481 int nextY = borderTop + (y * tiles.qHeight());
482 p.drawLine(borderLeft, nextY, borderLeft + (theBoard.m_width * tiles.qWidth()), nextY);
483 }
484
485 for (int x = 0; x <= theBoard.m_width; x++) {
486 int nextX = borderLeft + (x * tiles.qWidth());
487 p.drawLine(nextX, borderTop, nextX, borderTop + (theBoard.m_height * tiles.qHeight()));
488 }
489}
490
491void Editor::drawTiles(QPixmap *dest)
492{
493 QPainter p(dest);
494
495 int shadowX = tiles.width() - tiles.qWidth() * 2 - tiles.levelOffsetX();
496 int shadowY = tiles.height() - tiles.qHeight() * 2 - tiles.levelOffsetY();
497 short tile = 0;
498
499 int xOffset = -shadowX;
500 int yOffset = -tiles.levelOffsetY();
501
502 // we iterate over the depth stacking order. Each successive level is
503 // drawn one indent up and to the right. The indent is the width
504 // of the 3d relief on the tile left (tile shadow width)
505 for (int z = 0; z < theBoard.m_depth; z++) {
506 // we draw down the board so the tile below over rights our border
507 for (int y = 0; y < theBoard.m_height; y++) {
508 // drawing right to left to prevent border overwrite
509 for (int x = theBoard.m_width - 1; x >= 0; x--) {
510 int sx = x * tiles.qWidth() + xOffset + borderLeft;
511 int sy = y * tiles.qHeight() + yOffset + borderTop;
512
513 if (theBoard.getBoardData(z, y, x) != '1') {
514 continue;
515 }
516
517 QPixmap t;
518 tile = (z * theBoard.m_depth) + (y * theBoard.m_height) + (x * theBoard.m_width);
519// if (mode==remove && currPos.x==x && currPos.y==y && currPos.e==z) {
520// t = tiles.selectedPixmaps(44));
521// } else {
522 t = tiles.unselectedTile(0);
523// }
524
525 // Only one compilcation. Since we render top to bottom , left
526 // to right situations arise where...:
527 // there exists a tile one q height above and to the left
528 // in this situation we would draw our top left border over it
529 // we simply split the tile draw so the top half is drawn
530 // minus border
531 if ((x > 1) && (y > 0) && theBoard.getBoardData(z, y - 1, x - 2) == '1') {
532// p.drawPixmap( sx+tiles.levelOffsetX(), sy, t, tiles.levelOffsetX() , 0,
533// t.width() - tiles.levelOffsetX(), t.height() / 2);
534//
535// p.drawPixmap(sx, sy + t.height() / 2, t, 0, t.height() / 2, t.width(),
536// t.height() / 2);
537
538 p.drawPixmap(sx, sy, t, 0, 0, t.width(), t.height());
539
540 p.drawPixmap(sx - tiles.qWidth() + shadowX + tiles.levelOffsetX(), sy, t, t.width() - tiles.qWidth(), t.height() - tiles.qHeight() - tiles.levelOffsetX() - shadowY,
541 tiles.qWidth(), tiles.qHeight() + tiles.levelOffsetX());
542 } else {
543
544 p.drawPixmap(sx, sy, t, 0, 0, t.width(), t.height());
545 }
546
547 tile++;
548 tile = tile % 143;
549 }
550 }
551
552 xOffset += tiles.levelOffsetX();
553 yOffset -= tiles.levelOffsetY();
554 }
555}
556
557void Editor::transformPointToPosition(const QPoint &point, POSITION &MouseClickPos, bool align)
558{
559 // convert mouse position on screen to a tile z y x coord
560 // different to the one in kmahjongg.cpp since if we hit ground
561 // we return a result too.
562
563 short z = 0; // shut the compiler up about maybe uninitialised errors
564 short y = 0;
565 short x = 0;
566 MouseClickPos.e = 100;
567
568 // iterate over z coordinate from top to bottom
569 for (z = theBoard.m_depth - 1; z >= 0; --z) {
570 // calculate mouse coordiantes --> position in game board
571 // the factor -theTiles.width()/2 must keep track with the
572 // offset for blitting in the print zvent (FIX ME)
573 x = ((point.x() - borderLeft) - (z + 1) * tiles.levelOffsetX()) / tiles.qWidth();
574 y = ((point.y() - borderTop) + z * tiles.levelOffsetX()) / tiles.qHeight();
575
576 // skip when position is illegal
577 if (x < 0 || x >= theBoard.m_width || y < 0 || y >= theBoard.m_height) {
578 continue;
579 }
580
581 switch (theBoard.getBoardData(z, y, x)) {
582 case (UCHAR) '3':
583 if (align) {
584 x--;
585 y--;
586 }
587
588 break;
589
590 case (UCHAR) '2':
591 if (align) {
592 x--;
593 }
594
595 break;
596
597 case (UCHAR) '4':
598 if (align) {
599 y--;
600 }
601
602 break;
603
604 case (UCHAR) '1':
605 break;
606
607 default:
608 continue;
609 }
610
611 // if gameboard is empty, skip
612 if (!theBoard.getBoardData(z, y, x)) {
613 continue;
614 }
615
616 // here, position is legal
617 MouseClickPos.e = z;
618 MouseClickPos.y = y;
619 MouseClickPos.x = x;
620 MouseClickPos.f = theBoard.getBoardData(z, y, x);
621
622 break;
623 }
624
625 if (MouseClickPos.e == 100) {
626 MouseClickPos.x = x;
627 MouseClickPos.y = y;
628 MouseClickPos.f=0;
629 }
630}
631
632void Editor::drawFrameMousePressEvent( QMouseEvent* e )
633{
634 // we swallow the draw frames mouse clicks and process here
635
636 POSITION mPos;
637 transformPointToPosition(e->pos(), mPos, (mode == remove));
638
639 switch (mode) {
640 case remove:
641 if (!theBoard.tileAbove(mPos) && mPos.e < theBoard.m_depth && theBoard.isTileAt(mPos)) {
642 theBoard.deleteTile(mPos);
643 numTiles--;
644 statusChanged();
645 drawFrameMouseMovedEvent(e);
646 update();
647 }
648
649 break;
650 case insert: {
651 POSITION n = mPos;
652
653 if (n.e == 100) {
654 n.e = 0;
655 } else {
656 n.e += 1;
657 }
658
659 if (canInsert(n)) {
660 theBoard.insertTile(n);
661 clean = false;
662 numTiles++;
663 statusChanged();
664 update();
665 }
666
667 break;
668 }
669 default:
670 break;
671 }
672}
673
674void Editor::drawCursor(POSITION &p, bool visible)
675{
676 int x = borderLeft + (p.e * tiles.levelOffsetX()) + (p.x * tiles.qWidth());
677 int y = borderTop - ((p.e + 1) * tiles.levelOffsetY()) + (p.y * tiles.qHeight());
678 int w = (tiles.qWidth() * 2) + tiles.levelOffsetX();
679 int h = (tiles.qHeight() * 2) + tiles.levelOffsetY();
680
681 if (p.e == 100 || !visible) {
682 x = -1;
683 }
684
685 drawFrame->setRect(x, y, w, h, tiles.levelOffsetX(), mode-remove);
686 drawFrame->update();
687}
688
689void Editor::drawFrameMouseMovedEvent(QMouseEvent *e)
690{
691 // we swallow the draw frames mouse moves and process here
692
693 POSITION mPos;
694 transformPointToPosition(e->pos(), mPos, (mode == remove));
695
696 if ((mPos.x==currPos.x) && (mPos.y==currPos.y) && (mPos.e==currPos.e)) {
697 return;
698 }
699
700 currPos = mPos;
701
702 statusChanged();
703
704 switch(mode) {
705 case insert: {
706 POSITION next;
707 next = currPos;
708
709 if (next.e == 100) {
710 next.e = 0;
711 } else {
712 next.e += 1;
713 }
714
715 drawCursor(next, canInsert(next));
716
717 break;
718 }
719 case remove:
720 drawCursor(currPos, 1);
721
722 break;
723 case move:
724 break;
725 }
726}
727
728bool Editor::canInsert(POSITION &p)
729{
730 // can we inser a tile here. We can iff
731 // there are tiles in all positions below us (or we are a ground level)
732 // there are no tiles intersecting with us on this level
733
734 if (p.e >= theBoard.m_depth) {
735 return false;
736 }
737
738 if (p.y > theBoard.m_height - 2) {
739 return false;
740 }
741
742 if (p.x > theBoard.m_width - 2) {
743 return false;
744 }
745
746 POSITION n = p;
747
748 if (p.e != 0) {
749 n.e -= 1;
750 if (!theBoard.allFilled(n)) {
751 return false;
752 }
753 }
754
755 int any = theBoard.anyFilled(p);
756
757 return (!any);
758}
759
760void Editor::closeEvent(QCloseEvent *e)
761{
762 if (testSave()) {
763 theBoard.clearBoardLayout();
764 clean = true;
765 numTiles = 0;
766 statusChanged();
767 update();
768
769 // Save the window geometry.
770 Prefs::setEditorGeometry(geometry());
771 Prefs::self()->writeConfig();
772
773 e->accept();
774 } else {
775 e->ignore();
776 }
777}
778
779void Editor::setTilesetFromSettings()
780{
781 setTileset(Prefs::tileSet());
782}
783
784
785#include "Editor.moc"
786