|
|
|
#include <kapplication.h>
|
|
|
|
#include <kfiledialog.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <kmessagebox.h>
|
|
|
|
#include <kpushbutton.h>
|
|
|
|
#include <kstandarddirs.h>
|
|
|
|
#include <kstdguiitem.h>
|
|
|
|
#include <kimageio.h>
|
|
|
|
|
|
|
|
#include <tqcombobox.h>
|
|
|
|
#include <tqhgroupbox.h>
|
|
|
|
#include <tqimage.h>
|
|
|
|
#include <tqregexp.h>
|
|
|
|
#include <tqpainter.h>
|
|
|
|
#include <tqvbox.h>
|
|
|
|
|
|
|
|
#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(tqsizeHint());
|
|
|
|
}
|
|
|
|
|
|
|
|
Preview::~Preview()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void Preview::selectionChanged(int which)
|
|
|
|
{
|
|
|
|
m_selectedFile = m_fileList[which];
|
|
|
|
drawPreview();
|
|
|
|
m_drawFrame->tqrepaint(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 tqlayout = 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("*.tqlayout|Board Layout File (*.tqlayout)\n");
|
|
|
|
setCaption(i18n("Change Board Layout"));
|
|
|
|
m_selectedFile = tqlayout;
|
|
|
|
extension = "*.tqlayout";
|
|
|
|
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->tqrepaint(0,0,-1,-1,false);
|
|
|
|
markChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Top level preview drawing method. Background, tileset and tqlayout
|
|
|
|
// 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 tqlayout = Prefs::layout();
|
|
|
|
|
|
|
|
switch (m_previewType)
|
|
|
|
{
|
|
|
|
case background:
|
|
|
|
back = m_selectedFile;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case tileset:
|
|
|
|
tile = m_selectedFile;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case board:
|
|
|
|
tqlayout = m_selectedFile;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case theme:
|
|
|
|
// a theme is quite a bit of work. We load the
|
|
|
|
// specified bits in (tqlayout, 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
tqlayout = layoutRaw;
|
|
|
|
tqlayout.replace(":", "/kmahjongg/pics/");
|
|
|
|
if (!TQFile::exists(tqlayout))
|
|
|
|
{
|
|
|
|
tqlayout = layoutRaw;
|
|
|
|
tqlayout = "pics/" + tqlayout.right(tqlayout.length() - tqlayout.find(":") - 1);
|
|
|
|
tqlayout = locate("appdata", tqlayout);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_themeBack=back;
|
|
|
|
m_themeLayout=tqlayout;
|
|
|
|
m_themeTileset=tile;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
renderBackground(back);
|
|
|
|
renderTiles(tile, tqlayout);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Preview::paintEvent( TQPaintEvent* ){
|
|
|
|
m_drawFrame->tqrepaint(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 tqlayout 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 tqlayout 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 &tqlayout) {
|
|
|
|
m_tiles.loadTileset(file, true);
|
|
|
|
m_boardLayout.loadBoardLayout(tqlayout);
|
|
|
|
|
|
|
|
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<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*(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 tqlayout = 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);
|
|
|
|
tqlayout.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(),
|
|
|
|
tqlayout.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"
|