#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "prefs.h" #include "Preview.h" static const char * themeMagicV1_0= "kmahjongg-theme-v1.0"; Preview::Preview(TQWidget* parent) : KDialogBase(parent), m_tiles(true) { KPushButton *loadButton; TQGroupBox *group; TQVBox *page; page = new TQVBox(this); group = new TQHGroupBox(page); m_combo = new TQComboBox(false, group); connect(m_combo, TQT_SIGNAL(activated(int)), TQT_SLOT(selectionChanged(int))); loadButton = new KPushButton(i18n("Load..."), group); connect( loadButton, TQT_SIGNAL(clicked()), TQT_SLOT(load()) ); m_drawFrame = new FrameImage(page); m_drawFrame->setFixedSize(310, 236); m_changed = false; setMainWidget(page); setFixedSize(sizeHint()); } Preview::~Preview() { } void Preview::selectionChanged(int which) { m_selectedFile = m_fileList[which]; drawPreview(); m_drawFrame->repaint(0,0,-1,-1,false); markChanged(); } bool Preview::isChanged() { return m_changed; } void Preview::markChanged() { m_changed = true; } void Preview::markUnchanged() { m_changed = false; } void Preview::initialise(const PreviewType type) { TQString extension; TQString tile = Prefs::tileSet(); TQString back = Prefs::background(); TQString layout = Prefs::layout(); // set up the concept of the current file. Initialised to the preferences // value initially. Set the caption to indicate what we are doing switch (type) { case background: setCaption(i18n("Change Background Image")); m_selectedFile = back; m_fileSelector = i18n("*.bgnd|Background Image (*.bgnd)\n"); m_fileSelector += KImageIO::pattern()+"\n"; extension = "*.bgnd"; break; case tileset: setCaption(i18n("Change Tile Set")); m_fileSelector = i18n("*.tileset|Tile Set File (*.tileset)\n"); m_selectedFile = tile; extension = "*.tileset"; break; case board: m_fileSelector = i18n("*.layout|Board Layout File (*.layout)\n"); setCaption(i18n("Change Board Layout")); m_selectedFile = layout; extension = "*.layout"; break; case theme: m_fileSelector = i18n("*.theme|KMahjongg Theme File (*.theme)\n"); setCaption(i18n("Choose Theme")); m_selectedFile=""; extension = "*.theme"; m_themeLayout=""; m_themeBack=""; m_themeTileset=""; default: break; } m_fileSelector += i18n("*|All Files"); enableButtonApply(type != board); m_previewType = type; // we start with no change indicated markUnchanged(); m_fileList = kapp->dirs()->findAllResources("appdata", "pics/*"+extension, false, true); // get rid of files from the last invocation m_combo->clear(); TQStringList names; TQStringList::const_iterator it, itEnd; it = m_fileList.begin(); itEnd = m_fileList.end(); for ( ; it != itEnd; ++it) { TQFileInfo fi(*it); names << fi.baseName(); } m_combo->insertStringList(names); m_combo->setEnabled(m_fileList.count()); drawPreview(); } void Preview::slotApply() { if (isChanged()) { applyChange(); markUnchanged(); } } void Preview::slotOk() { slotApply(); accept(); } void Preview::load() { KURL url = KFileDialog::getOpenURL(TQString(), m_fileSelector, this, i18n("Open Board Layout" )); if ( !url.isEmpty() ) { m_selectedFile = url.path(); drawPreview(); m_drawFrame->repaint(0,0,-1,-1,false); markChanged(); } } // Top level preview drawing method. Background, tileset and layout // are initialised from the preferences. Depending on the type // of preview dialog we pick up the selected file for one of these // chaps. void Preview::drawPreview() { TQString tile = Prefs::tileSet(); TQString back = Prefs::background(); TQString layout = Prefs::layout(); switch (m_previewType) { case background: back = m_selectedFile; break; case tileset: tile = m_selectedFile; break; case board: layout = m_selectedFile; break; case theme: // a theme is quite a bit of work. We load the // specified bits in (layout, background and tileset if (!m_selectedFile.isEmpty()) { TQString backRaw, layoutRaw, tilesetRaw, magic; TQFile in(m_selectedFile); if (in.open(IO_ReadOnly)) { TQTextStream stream(&in); magic = stream.readLine(); if (magic != themeMagicV1_0) { in.close(); KMessageBox::sorry(this, i18n("That is not a valid theme file.")); break; } tilesetRaw = stream.readLine(); backRaw = stream.readLine(); layoutRaw = stream.readLine(); in.close(); tile = tilesetRaw; tile.replace(":", "/kmahjongg/pics/"); if (!TQFile::exists(tile)) { tile = tilesetRaw; tile = "pics/" + tile.right(tile.length() - tile.find(":") - 1 ); tile = locate("appdata", tile); } back = backRaw; back.replace(":", "/kmahjongg/pics/"); if (!TQFile::exists(back)) { back = backRaw; back = "pics/" + back.right(back.length() - back.find(":") - 1); back = locate("appdata", back); } layout = layoutRaw; layout.replace(":", "/kmahjongg/pics/"); if (!TQFile::exists(layout)) { layout = layoutRaw; layout = "pics/" + layout.right(layout.length() - layout.find(":") - 1); layout = locate("appdata", layout); } m_themeBack=back; m_themeLayout=layout; m_themeTileset=tile; } } break; } renderBackground(back); renderTiles(tile, layout); } void Preview::paintEvent( TQPaintEvent* ){ m_drawFrame->repaint(false); } // the user selected ok, or apply. This method passes the changes // across to the game widget and if necessary forces a board redraw // (unnecessary on layout changes since it only effects the next game) void Preview::applyChange() { switch (m_previewType) { case background: loadBackground(m_selectedFile, false); break; case tileset: loadTileset(m_selectedFile); break; case board: loadBoard(m_selectedFile); break; case theme: if (!m_themeLayout.isEmpty() && !m_themeBack.isEmpty() && !m_themeTileset.isEmpty()) { loadBackground(m_themeBack, false); loadTileset(m_themeTileset); loadBoard(m_themeLayout); } break; } // don't redraw for a layout change if (m_previewType == board || m_previewType == theme) layoutChange(); else boardRedraw(true); // either way we mark the current value as unchanged markUnchanged(); } // Render the background to the pixmap. void Preview::renderBackground(const TQString &bg) { TQImage img; TQImage tmp; TQPixmap *p; TQPixmap *b; p = m_drawFrame->getPreviewPixmap(); m_back.load(bg, p->width(), p->height()); b = m_back.getBackground(); bitBlt( p, 0,0, b,0,0, b->width(), b->height(), CopyROP ); } // This method draws a mini-tiled board with no tiles missing. void Preview::renderTiles(const TQString &file, const TQString &layout) { m_tiles.loadTileset(file, true); m_boardLayout.loadBoardLayout(layout); TQPixmap *dest = m_drawFrame->getPreviewPixmap(); int xOffset = m_tiles.width()/2; int yOffset = m_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=0; x--) { int sx = x*(m_tiles.qWidth() )+xOffset; int sy = y*(m_tiles.qHeight() )+yOffset; if (m_boardLayout.getBoardData(z, y, x) != '1') { continue; } TQPixmap *t = m_tiles.unselectedPixmaps(tile); // 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) && m_boardLayout.getBoardData(z,y-1,x-2)=='1'){ bitBlt( dest, sx+2, sy, t, 2,0, t->width(), 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++; if (tile == 35) tile++; tile = tile % 43; } } xOffset +=m_tiles.shadowSize(); yOffset -=m_tiles.shadowSize(); } } // this really does not belong here. It will be fixed in v1.1 onwards void Preview::saveTheme() { TQString tile = Prefs::tileSet(); TQString back = Prefs::background(); TQString layout = Prefs::layout(); TQString with = ":"; // we want to replace any path in the default store // with a + TQRegExp p(locate("data_dir", "/kmahjongg/pics/")); back.replace(p,with); tile.replace(p,with); layout.replace(p,with); // Get the name of the file to save KURL url = KFileDialog::getSaveURL( NULL, "*.theme", parentWidget(), i18n("Save Theme" )); if ( url.isEmpty() ) return; if( !url.isLocalFile() ) { KMessageBox::sorry( this, i18n( "Only saving to local files currently supported." ) ); return; } // Are we over writing an existin file, or was a directory selected? TQFileInfo f( url.path() ); if( f.isDir() ) return; 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?"),TQString(),i18n("Overwrite")); if (res != KMessageBox::Continue) return ; } FILE *outFile = fopen( TQFile::encodeName(url.path()), "w" ); if (outFile == NULL) { KMessageBox::sorry(this, i18n("Could not write to file. Aborting.")); return; } fprintf(outFile,"%s\n%s\n%s\n%s\n", themeMagicV1_0, tile.utf8().data(), back.utf8().data(), layout.utf8().data()); fclose(outFile); } FrameImage::FrameImage (TQWidget *parent, const char *name) : TQFrame(parent, name) { rx = -1; thePixmap = new TQPixmap(); } FrameImage::~FrameImage() { delete thePixmap; } void FrameImage::setGeometry(int x, int y, int w, int h) { TQFrame::setGeometry(x,y,w,h); thePixmap->resize(size()); } void FrameImage::paintEvent( TQPaintEvent* pa ) { TQFrame::paintEvent(pa); TQPainter p(this); TQPen line; line.setStyle(Qt::DotLine); line.setWidth(2); line.setColor(yellow); p.setPen(line); p.setBackgroundMode(Qt::OpaqueMode); p.setBackgroundColor(black); int x = pa->rect().left(); int y = pa->rect().top(); int h = pa->rect().height(); int w = pa->rect().width(); p.drawPixmap(x+frameWidth(),y+frameWidth(),*thePixmap,x+frameWidth(),y+frameWidth(),w-(2*frameWidth()),h-(2*frameWidth())); if (rx >=0) { p.drawRect(rx, ry, rw, rh); p.drawRect(rx+rs, ry, rw-rs, rh-rs); p.drawLine(rx, ry+rh, rx+rs, ry+rh-rs); int midX = rx+rs+((rw-rs)/2); int midY = ry+((rh-rs)/2); switch (rt) { case 0: // delete mode cursor p.drawLine(rx+rs, ry, rx+rw, ry+rh-rs); p.drawLine(rx+rw, ry, rx+rs, ry+rh-rs); break; case 1: // insert cursor p.drawLine(midX, ry, midX, ry+rh-rs); p.drawLine(rx+rs, midY, rx+rw, midY); break; case 2: // move mode cursor p.drawLine(midX, ry, rx+rw, midY); p.drawLine(rx+rw, midY, midX, ry+rh-rs); p.drawLine(midX, ry+rh-rs, rx+rs, midY); p.drawLine(rx+rs, midY, midX, ry); break; } } } void FrameImage::setRect(int x,int y,int w,int h, int s, int t) { rx = x; ry = y; rw = w; rh = h; rt = t; rs = s; } // Pass on the mouse presed event to our owner void FrameImage::mousePressEvent(TQMouseEvent *m) { mousePressed(m); } void FrameImage::mouseMoveEvent(TQMouseEvent *e) { mouseMoved(e); } #include "Preview.moc"