#include <stdlib.h> #include <tdeapplication.h> #include <tqlayout.h> #include <tqpainter.h> #include "Editor.h" #include "prefs.h" #include <tdemessagebox.h> #include <tdelocale.h> // Needed to use TDELocale #include <kiconloader.h> // #include <kstandarddirs.h> #include <tdetoolbarradiogroup.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 ( TQWidget* parent, const char* name ) : TQDialog( 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( TQ_NoFocus ); drawFrame->setBackgroundMode( TQWidget::PaletteBackground ); drawFrame->setFrameStyle( 49 ); drawFrame->setMouseTracking(true); // setup the tool bar setupToolbar(); TQVBoxLayout *layout = new TQVBoxLayout(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); TQString tile = Prefs::tileSet(); tiles.loadTileset(tile); // tell the user what we do setCaption(kapp->makeStdCaption(i18n("Edit Board Layout"))); connect( drawFrame, TQT_SIGNAL(mousePressed(TQMouseEvent *) ), TQT_SLOT(drawFrameMousePressEvent(TQMouseEvent *))); connect( drawFrame, TQT_SIGNAL(mouseMoved(TQMouseEvent *) ), TQT_SLOT(drawFrameMouseMovedEvent(TQMouseEvent *))); statusChanged(); update(); } Editor::~Editor() { } // --------------------------------------------------------- void Editor::setupToolbar() { TDEIconLoader *loader = TDEGlobal::iconLoader(); topToolbar = new TDEToolBar( this, "editToolBar" ); TDEToolBarRadioGroup *radio = new TDEToolBarRadioGroup(topToolbar); // new game topToolbar->insertButton(loader->loadIcon("document-new", TDEIcon::Toolbar), ID_TOOL_NEW, true, i18n("New board")); // open game topToolbar->insertButton(loader->loadIcon("document-open", TDEIcon::Toolbar), ID_TOOL_LOAD, true, i18n("Open board")); // save game topToolbar->insertButton(loader->loadIcon("document-save", TDEIcon::Toolbar), ID_TOOL_SAVE, true, i18n("Save board")); topToolbar->setButtonIconSet(ID_TOOL_SAVE,loader->loadIconSet("document-save", TDEIcon::Toolbar)); #ifdef FUTURE_OPTIONS // Select topToolbar->insertSeparator(); topToolbar->insertButton(loader->loadIcon("rectangle_select", TDEIcon::Toolbar), ID_TOOL_SELECT, true, i18n("Select")); topToolbar->insertButton(loader->loadIcon("edit-cut", TDEIcon::Toolbar), ID_TOOL_CUT, true, i18n("Cut")); topToolbar->insertButton(loader->loadIcon("edit-copy", TDEIcon::Toolbar), ID_TOOL_COPY, true, i18n("Copy")); topToolbar->insertButton(loader->loadIcon("edit-paste", TDEIcon::Toolbar), ID_TOOL_PASTE, true, i18n("Paste")); topToolbar->insertSeparator(); topToolbar->insertButton(loader->loadIcon("move", TDEIcon::Toolbar), ID_TOOL_MOVE, true, i18n("Move tiles")); #endif topToolbar->insertButton(loader->loadIcon("pencil", TDEIcon::Toolbar), ID_TOOL_ADD, true, i18n("Add tiles")); topToolbar->insertButton(loader->loadIcon("edit-delete", TDEIcon::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", TDEIcon::Toolbar), ID_TOOL_LEFT, true, i18n("Shift left")); topToolbar->insertButton(loader->loadIcon("go-up", TDEIcon::Toolbar), ID_TOOL_UP, true, i18n("Shift up")); topToolbar->insertButton(loader->loadIcon("go-down", TDEIcon::Toolbar), ID_TOOL_DOWN, true, i18n("Shift down")); topToolbar->insertButton(loader->loadIcon("forward", TDEIcon::Toolbar), ID_TOOL_RIGHT, true, i18n("Shift right")); topToolbar->insertSeparator(); topToolbar->insertButton(loader->loadIcon("system-log-out", TDEIcon::Toolbar), ID_META_EXIT, true, i18n("Exit")); // status in the toolbar for now (ick) theLabel = new TQLabel(statusText(), topToolbar); int lWidth = theLabel->sizeHint().width(); topToolbar->insertWidget(ID_TOOL_STATUS,lWidth, theLabel ); topToolbar->alignItemRight( ID_TOOL_STATUS, true ); //addToolBar(topToolbar); connect( topToolbar, TQT_SIGNAL(clicked(int) ), TQT_SLOT( topToolbarOption(int) ) ); topToolbar->updateRects(0); topToolbar->setFullSize(true); topToolbar->setBarPos(TDEToolBar::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; } } TQString Editor::statusText() { TQString 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; TQFileInfo 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?"),TQString(),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( TQPaintEvent* ) { // first we layer on a background grid TQPixmap buff; TQPixmap *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(TQPixmap *pixmap) { TQPainter p(pixmap); // blast in a white background p.fillRect(0,0,pixmap->width(), pixmap->height(), TQColor(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(TQPixmap *dest) { TQPainter p(dest); TQString 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; } TQPixmap *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 TQPoint& 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( TQMouseEvent* 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( TQMouseEvent* 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"