You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegames/kmahjongg/Editor.cpp

676 lines
16 KiB

#include <stdlib.h>
#include <kapplication.h>
#include <qlayout.h>
#include <qpainter.h>
#include "Editor.h"
#include "prefs.h"
#include <kmessagebox.h>
#include <klocale.h> // Needed to use KLocale
#include <kiconloader.h> //
#include <kstandarddirs.h>
#include <ktoolbarradiogroup.h>
#define ID_TOOL_NEW 100
#define ID_TOOL_LOAD 101
#define ID_TOOL_SAVE 102
#define ID_TOOL_ADD 103
#define ID_TOOL_DEL 104
#define ID_TOOL_MOVE 105
#define ID_TOOL_SELECT 106
#define ID_TOOL_CUT 107
#define ID_TOOL_COPY 108
#define ID_TOOL_PASTE 109
#define ID_TOOL_LEFT 110
#define ID_TOOL_RIGHT 111
#define ID_TOOL_UP 112
#define ID_TOOL_DOWN 113
#define ID_TOOL_STATUS 199
#define ID_META_EXIT 201
// When we assign a tile to draw in a slot we do it in order from te following
// table, wrapping on the tile number. It makes the tile layout look more
// random.
Editor::Editor
(
QWidget* parent,
const char* name
)
:
QDialog( parent, name, true, 0 ), tiles(false)
{
clean= true;
numTiles=0;
mode = insert;
int sWidth = (BoardLayout::width+2)*(tiles.qWidth());
int sHeight =( BoardLayout::height+2)*tiles.qHeight();
sWidth += 4*tiles.shadowSize();
drawFrame = new FrameImage( this, "drawFrame" );
drawFrame->setGeometry( 10, 40 ,sWidth ,sHeight);
drawFrame->setMinimumSize( 0, 0 );
drawFrame->setMaximumSize( 32767, 32767 );
drawFrame->setFocusPolicy( QWidget::NoFocus );
drawFrame->setBackgroundMode( QWidget::PaletteBackground );
drawFrame->setFrameStyle( 49 );
drawFrame->setMouseTracking(true);
// setup the tool bar
setupToolbar();
QVBoxLayout *layout = new QVBoxLayout(this, 1);
layout->addWidget(topToolbar,0);
layout->addWidget(drawFrame,1);
layout->activate();
resize( sWidth+60, sHeight+60);
setMinimumSize( sWidth+60, sHeight+60);
setMaximumSize( sWidth+60, sHeight+60);
QString tile = Prefs::tileSet();
tiles.loadTileset(tile);
// tell the user what we do
setCaption(kapp->makeStdCaption(i18n("Edit Board Layout")));
connect( drawFrame, SIGNAL(mousePressed(QMouseEvent *) ),
SLOT(drawFrameMousePressEvent(QMouseEvent *)));
connect( drawFrame, SIGNAL(mouseMoved(QMouseEvent *) ),
SLOT(drawFrameMouseMovedEvent(QMouseEvent *)));
statusChanged();
update();
}
Editor::~Editor()
{
}
// ---------------------------------------------------------
void Editor::setupToolbar()
{
KIconLoader *loader = KGlobal::iconLoader();
topToolbar = new KToolBar( this, "editToolBar" );
KToolBarRadioGroup *radio = new KToolBarRadioGroup(topToolbar);
// new game
topToolbar->insertButton(loader->loadIcon("filenew", KIcon::Toolbar),
ID_TOOL_NEW, true, i18n("New board"));
// open game
topToolbar->insertButton(loader->loadIcon("fileopen", KIcon::Toolbar),
ID_TOOL_LOAD, true, i18n("Open board"));
// save game
topToolbar->insertButton(loader->loadIcon("filesave", KIcon::Toolbar),
ID_TOOL_SAVE, true, i18n("Save board"));
topToolbar->setButtonIconSet(ID_TOOL_SAVE,loader->loadIconSet("filesave", KIcon::Toolbar));
#ifdef FUTURE_OPTIONS
// Select
topToolbar->insertSeparator();
topToolbar->insertButton(loader->loadIcon("rectangle_select", KIcon::Toolbar),
ID_TOOL_SELECT, true, i18n("Select"));
topToolbar->insertButton(loader->loadIcon("editcut", KIcon::Toolbar),
ID_TOOL_CUT, true, i18n("Cut"));
topToolbar->insertButton(loader->loadIcon("editcopy", KIcon::Toolbar),
ID_TOOL_COPY, true, i18n("Copy"));
topToolbar->insertButton(loader->loadIcon("editpaste", KIcon::Toolbar),
ID_TOOL_PASTE, true, i18n("Paste"));
topToolbar->insertSeparator();
topToolbar->insertButton(loader->loadIcon("move", KIcon::Toolbar),
ID_TOOL_MOVE, true, i18n("Move tiles"));
#endif
topToolbar->insertButton(loader->loadIcon("pencil", KIcon::Toolbar),
ID_TOOL_ADD, true, i18n("Add tiles"));
topToolbar->insertButton(loader->loadIcon("editdelete", KIcon::Toolbar),
ID_TOOL_DEL, true, i18n("Remove tiles"));
topToolbar->setToggle(ID_TOOL_ADD);
topToolbar->setToggle(ID_TOOL_MOVE);
topToolbar->setToggle(ID_TOOL_DEL);
topToolbar->toggleButton(ID_TOOL_ADD);
radio->addButton(ID_TOOL_ADD);
#ifdef FUTURE_OPTIONS
radio->addButton(ID_TOOL_MOVE);
#endif
radio->addButton(ID_TOOL_DEL);
// board shift
topToolbar->insertSeparator();
topToolbar->insertButton(loader->loadIcon("back", KIcon::Toolbar),
ID_TOOL_LEFT, true, i18n("Shift left"));
topToolbar->insertButton(loader->loadIcon("up", KIcon::Toolbar),
ID_TOOL_UP, true, i18n("Shift up"));
topToolbar->insertButton(loader->loadIcon("down", KIcon::Toolbar),
ID_TOOL_DOWN, true, i18n("Shift down"));
topToolbar->insertButton(loader->loadIcon("forward", KIcon::Toolbar),
ID_TOOL_RIGHT, true, i18n("Shift right"));
topToolbar->insertSeparator();
topToolbar->insertButton(loader->loadIcon("exit", KIcon::Toolbar),
ID_META_EXIT, true, i18n("Exit"));
// status in the toolbar for now (ick)
theLabel = new QLabel(statusText(), topToolbar);
int lWidth = theLabel->sizeHint().width();
topToolbar->insertWidget(ID_TOOL_STATUS,lWidth, theLabel );
topToolbar->alignItemRight( ID_TOOL_STATUS, true );
//addToolBar(topToolbar);
connect( topToolbar, SIGNAL(clicked(int) ), SLOT( topToolbarOption(int) ) );
topToolbar->updateRects(0);
topToolbar->setFullSize(true);
topToolbar->setBarPos(KToolBar::Top);
// topToolbar->enableMoving(false);
topToolbar->adjustSize();
setMinimumWidth(topToolbar->width());
}
void Editor::statusChanged() {
bool canSave = ((numTiles !=0) && ((numTiles & 1) == 0));
theLabel->setText(statusText());
topToolbar->setItemEnabled( ID_TOOL_SAVE, canSave);
}
void Editor::topToolbarOption(int option) {
switch(option) {
case ID_TOOL_NEW:
newBoard();
break;
case ID_TOOL_LOAD:
loadBoard();
break;
case ID_TOOL_SAVE:
saveBoard();
break;
case ID_TOOL_LEFT:
theBoard.shiftLeft();
repaint(false);
break;
case ID_TOOL_RIGHT:
theBoard.shiftRight();
repaint(false);
break;
case ID_TOOL_UP:
theBoard.shiftUp();
repaint(false);
break;
case ID_TOOL_DOWN:
theBoard.shiftDown();
repaint(false);
break;
case ID_TOOL_DEL:
mode=remove;
break;
case ID_TOOL_MOVE:
mode=move;
break;
case ID_TOOL_ADD:
mode = insert;
break;
case ID_META_EXIT:
close();
break;
default:
break;
}
}
QString Editor::statusText() {
QString buf;
int x=currPos.x;
int y=currPos.y;
int z= currPos.e;
if (z == 100)
z = 0;
else
z=z+1;
if (x >=BoardLayout::width || x <0 || y >=BoardLayout::height || y <0)
x = y = z = 0;
buf = i18n("Tiles: %1 Pos: %2,%3,%4").arg(numTiles).arg(x).arg(y).arg(z);
return buf;
}
void Editor::loadBoard() {
if ( !testSave() )
return;
KURL url = KFileDialog::getOpenURL(
NULL,
i18n("*.layout|Board Layout (*.layout)\n"
"*|All Files"),
this,
i18n("Open Board Layout" ));
if ( url.isEmpty() )
return;
theBoard.loadBoardLayout( url.path() );
repaint(false);
}
// Clear out the contents of the board. Repaint the screen
// set values to their defaults.
void Editor::newBoard() {
if (!testSave())
return;
theBoard.clearBoardLayout();
clean=true;
numTiles=0;
statusChanged();
repaint(false);
}
bool Editor::saveBoard() {
// get a save file name
KURL url = KFileDialog::getSaveURL(
NULL,
i18n("*.layout|Board Layout (*.layout)\n"
"*|All Files"),
this,
i18n("Save Board Layout" ));
if( url.isEmpty() ) return false;
if( !url.isLocalFile() )
{
KMessageBox::sorry( this, i18n( "Only saving to local files currently supported." ) );
return false;
}
if ( url.isEmpty() )
return false;
QFileInfo f( url.path() );
if ( f.exists() ) {
// if it already exists, querie the user for replacement
int res=KMessageBox::warningContinueCancel(this,
i18n("A file with that name "
"already exists. Do you "
"wish to overwrite it?"),
i18n("Save Board Layout" ), KStdGuiItem::save());
if (res != KMessageBox::Continue)
return false;
}
bool result = theBoard.saveBoardLayout( url.path() );
if (result==true){
clean = true;
return true;
} else {
return false;
}
}
// test if a save is required and return true if the app is to continue
// false if cancel is selected. (if ok then call out to save the board
bool Editor::testSave()
{
if (clean)
return(true);
int res;
res=KMessageBox::warningYesNoCancel(this,
i18n("The board has been modified. Would you "
"like to save the changes?"),QString::null,KStdGuiItem::save(),KStdGuiItem::dontSave());
if (res == KMessageBox::Yes) {
// yes to save
if (saveBoard()) {
return true;
} else {
KMessageBox::sorry(this, i18n("Save failed. Aborting operation."));
}
} else {
return (res != KMessageBox::Cancel);
}
return(true);
}
// The main paint event, draw in the grid and blit in
// the tiles as specified by the layout.
void Editor::paintEvent( QPaintEvent* ) {
// first we layer on a background grid
QPixmap buff;
QPixmap *dest=drawFrame->getPreviewPixmap();
buff.resize(dest->width(), dest->height());
drawBackground(&buff);
drawTiles(&buff);
bitBlt(dest, 0,0,&buff, 0,0,buff.width(), buff.height(), CopyROP);
drawFrame->repaint(false);
}
void Editor::drawBackground(QPixmap *pixmap) {
QPainter p(pixmap);
// blast in a white background
p.fillRect(0,0,pixmap->width(), pixmap->height(), QColor(white));
// now put in a grid of tile quater width squares
int sy = (tiles.height()/2)+tiles.shadowSize();
int sx = (tiles.width()/2);
for (int y=0; y<=BoardLayout::height; y++) {
int nextY=sy+(y*tiles.qHeight());
p.drawLine(sx, nextY,sx+(BoardLayout::width*tiles.qWidth()), nextY);
}
for (int x=0; x<=BoardLayout::width; x++) {
int nextX=sx+(x*tiles.qWidth());
p.drawLine(nextX, sy, nextX, sy+BoardLayout::height*tiles.qHeight());
}
}
void Editor::drawTiles(QPixmap *dest) {
QPainter p(dest);
QString tile1 = Prefs::tileSet();
tiles.loadTileset(tile1);
int xOffset = tiles.width()/2;
int yOffset = tiles.height()/2;
short tile = 0;
// we iterate over the depth stacking order. Each successive level is
// drawn one indent up and to the right. The indent is the width
// of the 3d relief on the tile left (tile shadow width)
for (int z=0; z<BoardLayout::depth; z++) {
// we draw down the board so the tile below over rights our border
for (int y = 0; y < BoardLayout::height; y++) {
// drawing right to left to prevent border overwrite
for (int x=BoardLayout::width-1; x>=0; x--) {
int sx = x*(tiles.qWidth() )+xOffset;
int sy = y*(tiles.qHeight() )+yOffset;
if (theBoard.getBoardData(z, y, x) != '1') {
continue;
}
QPixmap *t;
tile=(z*BoardLayout::depth)+
(y*BoardLayout::height)+
(x*BoardLayout::width);
// if (mode==remove && currPos.x==x && currPos.y==y && currPos.e==z) {
// t = tiles.selectedPixmaps(44));
// } else {
t = tiles.unselectedPixmaps(43);
// }
// Only one compilcation. Since we render top to bottom , left
// to right situations arise where...:
// there exists a tile one q height above and to the left
// in this situation we would draw our top left border over it
// we simply split the tile draw so the top half is drawn
// minus border
if ((x>1) && (y>0) && theBoard.getBoardData(z,y-1,x-2)=='1'){
bitBlt( dest,
sx+tiles.shadowSize(), sy,
t, tiles.shadowSize() ,0,
t->width()-tiles.shadowSize(),
t->height()/2, CopyROP );
bitBlt( dest, sx, sy+t->height()/2,
t, 0,t->height()/2,t->width(),t->height()/2,CopyROP);
} else {
bitBlt( dest, sx, sy,
t, 0,0, t->width(), t->height(), CopyROP );
}
tile++;
tile = tile % 143;
}
}
xOffset +=tiles.shadowSize();
yOffset -=tiles.shadowSize();
}
}
// convert mouse position on screen to a tile z y x coord
// different to the one in kmahjongg.cpp since if we hit ground
// we return a result too.
void Editor::transformPointToPosition(
const QPoint& point,
POSITION& MouseClickPos,
bool align)
{
short z = 0; // shut the compiler up about maybe uninitialised errors
short y = 0;
short x = 0;
MouseClickPos.e = 100;
// iterate over z coordinate from top to bottom
for( z=BoardLayout::depth-1; z>=0; z-- )
{
// calculate mouse coordiantes --> position in game board
// the factor -theTiles.width()/2 must keep track with the
// offset for blitting in the print zvent (FIX ME)
x = ((point.x()-tiles.width()/2)-(z+1)*tiles.shadowSize())/ tiles.qWidth();
y = ((point.y()-tiles.height()/2)+ z*tiles.shadowSize()) / tiles.qHeight();
// skip when position is illegal
if (x<0 || x>=BoardLayout::width || y<0 || y>=BoardLayout::height)
continue;
//
switch( theBoard.getBoardData(z,y,x) )
{
case (UCHAR)'3': if (align) {
x--;
y--;
}
break;
case (UCHAR)'2': if (align)
x--;
break;
case (UCHAR)'4': if (align)
y--;
break;
case (UCHAR)'1': break;
default : continue;
}
// if gameboard is empty, skip
if ( ! theBoard.getBoardData(z,y,x) )
continue;
// here, position is legal
MouseClickPos.e = z;
MouseClickPos.y = y;
MouseClickPos.x = x;
MouseClickPos.f = theBoard.getBoardData(z,y,x);
break;
}
if (MouseClickPos.e == 100) {
MouseClickPos.x = x;
MouseClickPos.y = y;
MouseClickPos.f=0;
}
}
// we swallow the draw frames mouse clicks and process here
void Editor::drawFrameMousePressEvent( QMouseEvent* e )
{
POSITION mPos;
transformPointToPosition(e->pos(), mPos, (mode == remove));
switch (mode) {
case remove:
if (!theBoard.tileAbove(mPos) && mPos.e < BoardLayout::depth && theBoard.isTileAt(mPos) ) {
theBoard.deleteTile(mPos);
numTiles--;
statusChanged();
drawFrameMouseMovedEvent(e);
repaint(false);
}
break;
case insert: {
POSITION n = mPos;
if (n.e == 100)
n.e = 0;
else
n.e += 1;
if (canInsert(n)) {
theBoard.insertTile(n);
numTiles++;
statusChanged();
repaint(false);
}
}
break;
default:
break;
}
}
void Editor::drawCursor(POSITION &p, bool visible)
{
int x = (tiles.width()/2)+(p.e*tiles.shadowSize())+(p.x * tiles.qWidth());
int y = (tiles.height()/2)-(p.e*tiles.shadowSize())+(p.y * tiles.qHeight());
int w = tiles.width();
int h = tiles.height();
if (p.e==100 || !visible)
x = -1;
drawFrame->setRect(x,y,w,h, tiles.shadowSize(), mode-remove);
drawFrame->repaint(false);
}
// we swallow the draw frames mouse moves and process here
void Editor::drawFrameMouseMovedEvent( QMouseEvent* e ){
POSITION mPos;
transformPointToPosition(e->pos(), mPos, (mode == remove));
if ((mPos.x==currPos.x) && (mPos.y==currPos.y) && (mPos.e==currPos.e))
return;
currPos = mPos;
statusChanged();
switch(mode) {
case insert: {
POSITION next;
next = currPos;
if (next.e == 100)
next.e = 0;
else
next.e += 1;
drawCursor(next, canInsert(next));
break;
}
case remove:
drawCursor(currPos, 1);
break;
case move:
break;
}
}
// can we inser a tile here. We can iff
// there are tiles in all positions below us (or we are a ground level)
// there are no tiles intersecting with us on this level
bool Editor::canInsert(POSITION &p) {
if (p.e >= BoardLayout::depth)
return (false);
if (p.y >BoardLayout::height-2)
return false;
if (p.x >BoardLayout::width-2)
return false;
POSITION n = p;
if (p.e != 0) {
n.e -= 1;
if (!theBoard.allFilled(n)) {
return(false);
}
}
int any = theBoard.anyFilled(p);
return(!any);
}
#include "Editor.moc"