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.
561 lines
18 KiB
561 lines
18 KiB
/*
|
|
patience -- main program
|
|
Copyright (C) 1995 Paul Olav Tvete
|
|
|
|
Permission to use, copy, modify, and distribute this software and its
|
|
documentation for any purpose and without fee is hereby granted,
|
|
provided that the above copyright notice appear in all copies and that
|
|
both that copyright notice and this permission notice appear in
|
|
supporting documentation.
|
|
|
|
This file is provided AS IS with no warranties of any kind. The author
|
|
shall have no liability with respect to the infringement of copyrights,
|
|
trade secrets or any patents by this file or any part thereof. In no
|
|
event will the author be liable for any lost revenue or profits or
|
|
other special, indirect and consequential damages.
|
|
|
|
|
|
Heavily modified by Mario Weilguni <mweilguni@sime.com>
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <tqregexp.h>
|
|
#include <tqtimer.h>
|
|
#include <tqimage.h>
|
|
|
|
#include <kapplication.h>
|
|
#include <klocale.h>
|
|
#include <kaction.h>
|
|
#include <kdebug.h>
|
|
#include <kcarddialog.h>
|
|
#include <kinputdialog.h>
|
|
#include <kstandarddirs.h>
|
|
#include <tdefiledialog.h>
|
|
#include <ktempfile.h>
|
|
#include <tdeio/netaccess.h>
|
|
#include <kmessagebox.h>
|
|
#include <kstatusbar.h>
|
|
#include <kaccelmanager.h>
|
|
#include <kmenubar.h>
|
|
|
|
#include "pwidget.h"
|
|
#include "version.h"
|
|
#include "dealer.h"
|
|
#include "cardmaps.h"
|
|
#include "speeds.h"
|
|
#include "gamestatsimpl.h"
|
|
|
|
|
|
static pWidget *current_pwidget = 0;
|
|
|
|
|
|
void saveGame(int) {
|
|
current_pwidget->saveGame();
|
|
}
|
|
|
|
pWidget::pWidget()
|
|
: KMainWindow(0, "pwidget"), dill(0)
|
|
{
|
|
current_pwidget = this;
|
|
// KCrash::setEmergencySaveFunction(::saveGame);
|
|
KStdAction::quit(TQT_TQOBJECT(kapp), TQT_SLOT(quit()), actionCollection(), "game_exit");
|
|
|
|
undo = KStdAction::undo(TQT_TQOBJECT(this), TQT_SLOT(undoMove()),
|
|
actionCollection(), "undo_move");
|
|
undo->setEnabled(false);
|
|
(void)KStdAction::openNew(TQT_TQOBJECT(this), TQT_SLOT(newGame()),
|
|
actionCollection(), "new_game");
|
|
(void)KStdAction::open(TQT_TQOBJECT(this), TQT_SLOT(openGame()),
|
|
actionCollection(), "open");
|
|
recent = KStdAction::openRecent(TQT_TQOBJECT(this), TQT_SLOT(openGame(const KURL&)),
|
|
actionCollection(), "open_recent");
|
|
recent->loadEntries(TDEGlobal::config());
|
|
(void)KStdAction::saveAs(TQT_TQOBJECT(this), TQT_SLOT(saveGame()),
|
|
actionCollection(), "save");
|
|
(void)new KAction(i18n("&Choose Game..."), 0, TQT_TQOBJECT(this), TQT_SLOT(chooseGame()),
|
|
actionCollection(), "choose_game");
|
|
(void)new KAction(i18n("Restart &Game"), TQString::fromLatin1("reload"), 0,
|
|
TQT_TQOBJECT(this), TQT_SLOT(restart()),
|
|
actionCollection(), "restart_game");
|
|
(void)KStdAction::help(TQT_TQOBJECT(this), TQT_SLOT(helpGame()), actionCollection(), "help_game");
|
|
|
|
games = new KSelectAction(i18n("&Game Type"), 0, TQT_TQOBJECT(this),
|
|
TQT_SLOT(newGameType()),
|
|
actionCollection(), "game_type");
|
|
TQStringList list;
|
|
TQValueList<DealerInfo*>::ConstIterator it;
|
|
uint max_type = 0;
|
|
|
|
for (it = DealerInfoList::self()->games().begin();
|
|
it != DealerInfoList::self()->games().end(); ++it)
|
|
{
|
|
// while we develop, it may happen that some lower
|
|
// indices do not exist
|
|
uint index = (*it)->gameindex;
|
|
for (uint i = 0; i <= index; i++)
|
|
if (list.count() <= i)
|
|
list.append("unknown");
|
|
list[index] = i18n((*it)->name);
|
|
if (max_type < index)
|
|
max_type = index;
|
|
}
|
|
games->setItems(list);
|
|
|
|
TDEGlobal::dirs()->addResourceType("wallpaper", KStandardDirs::kde_default("data") + "kpat/backgrounds/");
|
|
TDEGlobal::dirs()->addResourceType("wallpaper", KStandardDirs::kde_default("data") + "ksnake/backgrounds/");
|
|
wallpapers = new KSelectAction(i18n("&Change Background"), 0, TQT_TQOBJECT(this),
|
|
TQT_SLOT(changeWallpaper()),
|
|
actionCollection(), "wallpaper");
|
|
list.clear();
|
|
wallpaperlist.clear();
|
|
TQStringList wallpaperlist2 = TDEGlobal::dirs()->findAllResources("wallpaper", TQString(),
|
|
false, true, list);
|
|
TQStringList list2;
|
|
for (TQStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
|
|
TQString file = *it;
|
|
int rindex = file.findRev('.');
|
|
if (rindex != -1) {
|
|
TQString ext = file.mid(rindex + 1).lower();
|
|
if (ext == "jpeg" || ext == "png" || ext == "jpg") {
|
|
list2.append(file.left(rindex));
|
|
wallpaperlist.append( file );
|
|
}
|
|
}
|
|
}
|
|
|
|
wallpapers->setItems(list2);
|
|
wallpapers->setCurrentItem(list2.findIndex("No-Ones-Laughing-3"));
|
|
|
|
changeWallpaper();
|
|
|
|
(void)new cardMap(midcolor);
|
|
|
|
backs = new KAction(i18n("&Switch Cards..."), 0, TQT_TQOBJECT(this),
|
|
TQT_SLOT(changeBackside()),
|
|
actionCollection(), "backside");
|
|
stats = new KAction(i18n("&Statistics"), 0, TQT_TQOBJECT(this), TQT_SLOT(showStats()),
|
|
actionCollection(),"game_stats");
|
|
|
|
animation = new KToggleAction(i18n( "&Animation on Startup" ),
|
|
0, TQT_TQOBJECT(this), TQT_SLOT(animationChanged()),
|
|
actionCollection(), "animation");
|
|
dropaction = new KToggleAction(i18n("&Enable Autodrop"),
|
|
0, TQT_TQOBJECT(this), TQT_SLOT(enableAutoDrop()),
|
|
actionCollection(), "enable_autodrop");
|
|
dropaction->setCheckedState(i18n("Disable Autodrop"));
|
|
|
|
TDEConfig *config = kapp->config();
|
|
TDEConfigGroupSaver cs(config, settings_group );
|
|
|
|
TQString bgpath = config->readPathEntry("Background");
|
|
kdDebug(11111) << "bgpath '" << bgpath << "'" << endl;
|
|
if (bgpath.isEmpty())
|
|
bgpath = locate("wallpaper", "No-Ones-Laughing-3.jpg");
|
|
background = TQPixmap(bgpath);
|
|
|
|
bool animate = config->readBoolEntry( "Animation", true);
|
|
animation->setChecked( animate );
|
|
|
|
bool autodrop = config->readBoolEntry("Autodrop", true);
|
|
dropaction->setChecked(autodrop);
|
|
|
|
uint game = config->readNumEntry("DefaultGame", 0);
|
|
if (game > max_type)
|
|
game = max_type;
|
|
games->setCurrentItem(game);
|
|
|
|
statusBar()->insertItem( "", 1, 0, true );
|
|
|
|
createGUI(TQString(), false);
|
|
KAcceleratorManager::manage(menuBar());
|
|
|
|
newGameType();
|
|
adjustSize();
|
|
setAutoSaveSettings();
|
|
}
|
|
|
|
pWidget::~pWidget()
|
|
{
|
|
delete dill;
|
|
}
|
|
|
|
void pWidget::undoMove() {
|
|
if( dill )
|
|
dill->undo();
|
|
}
|
|
|
|
void pWidget::helpGame()
|
|
{
|
|
if (!dill)
|
|
return;
|
|
kapp->invokeHelp(dill->anchorName());
|
|
}
|
|
|
|
void pWidget::undoPossible(bool poss)
|
|
{
|
|
undo->setEnabled(poss);
|
|
}
|
|
|
|
void pWidget::changeBackside() {
|
|
TDEConfig *config = kapp->config();
|
|
TDEConfigGroupSaver kcs(config, settings_group);
|
|
|
|
TQString deck = config->readEntry("Back", KCardDialog::getDefaultDeck());
|
|
TQString cards = config->readEntry("Cards", KCardDialog::getDefaultCardDir());
|
|
if (KCardDialog::getCardDeck(deck, cards, this, KCardDialog::Both) == TQDialog::Accepted)
|
|
{
|
|
TQString imgname = KCardDialog::getCardPath(cards, 11);
|
|
|
|
TQImage image;
|
|
image.load(imgname);
|
|
if( image.isNull()) {
|
|
kdDebug(11111) << "cannot load card pixmap \"" << imgname << "\" in " << cards << "\n";
|
|
return;
|
|
}
|
|
|
|
bool change = false;
|
|
if (image.width() != cardMap::CARDX() || image.height() != cardMap::CARDY())
|
|
{
|
|
change = true;
|
|
if (KMessageBox::warningContinueCancel(this, i18n("The cards you have chosen have a different "
|
|
"size than the ones you are currently using. "
|
|
"This requires the current game to be restarted.")) == KMessageBox::Cancel)
|
|
return;
|
|
}
|
|
setBackSide(deck, cards);
|
|
if (change) {
|
|
|
|
newGameType();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void pWidget::changeWallpaper()
|
|
{
|
|
TQString bgpath=locate("wallpaper", wallpaperlist[wallpapers->currentItem()]);
|
|
if (bgpath.isEmpty())
|
|
return;
|
|
background = TQPixmap(bgpath);
|
|
if (background.isNull()) {
|
|
KMessageBox::sorry(this, i18n("<qt>Couldn't load wallpaper<br/>%1</qt>").arg(bgpath));
|
|
return;
|
|
}
|
|
|
|
TQImage bg = background.convertToImage().convertDepth(8, 0);
|
|
if (bg.isNull() || !bg.numColors())
|
|
return;
|
|
long r = 0;
|
|
long g = 0;
|
|
long b = 0;
|
|
for (int i = 0; i < bg.numColors(); ++i)
|
|
{
|
|
TQRgb rgb = bg.color(i);
|
|
r += tqRed(rgb);
|
|
g += tqGreen(rgb);
|
|
b += tqBlue(rgb);
|
|
}
|
|
r /= bg.numColors();
|
|
b /= bg.numColors();
|
|
g /= bg.numColors();
|
|
midcolor = TQColor(r, b, g);
|
|
|
|
if (dill) {
|
|
TDEConfig *config = kapp->config();
|
|
TDEConfigGroupSaver kcs(config, settings_group);
|
|
|
|
TQString deck = config->readEntry("Back", KCardDialog::getDefaultDeck());
|
|
TQString dummy = config->readEntry("Cards", KCardDialog::getDefaultCardDir());
|
|
setBackSide(deck, dummy);
|
|
|
|
config->writePathEntry("Background", bgpath);
|
|
dill->setBackgroundPixmap(background, midcolor);
|
|
dill->canvas()->setAllChanged();
|
|
dill->canvas()->update();
|
|
}
|
|
}
|
|
|
|
void pWidget::animationChanged() {
|
|
bool anim = animation->isChecked();
|
|
TDEConfig *config = kapp->config();
|
|
TDEConfigGroupSaver cs(config, settings_group );
|
|
config->writeEntry( "Animation", anim);
|
|
}
|
|
|
|
void pWidget::enableAutoDrop()
|
|
{
|
|
bool drop = dropaction->isChecked();
|
|
TDEConfig *config = kapp->config();
|
|
TDEConfigGroupSaver cs(config, settings_group );
|
|
config->writeEntry( "Autodrop", drop);
|
|
dill->setAutoDropEnabled(drop);
|
|
}
|
|
|
|
void pWidget::newGame()
|
|
{
|
|
// Check if the user is already running a game, and if she is,
|
|
// then ask if she wants to abort it.
|
|
if (!dill->isGameWon() && !dill->isGameLost()
|
|
&& KMessageBox::warningContinueCancel(0,
|
|
i18n("You are already running an unfinished game. "
|
|
"If you abort the old game to start a new one, "
|
|
"the old game will be registered as a loss in "
|
|
"the statistics file.\n"
|
|
"What do you want to do?"),
|
|
i18n("Abort Current Game?"),
|
|
i18n("Abort Old Game"),
|
|
"careaboutstats" ) == KMessageBox::Cancel)
|
|
return;
|
|
|
|
dill->setGameNumber(kapp->random());
|
|
setGameCaption();
|
|
restart();
|
|
}
|
|
|
|
|
|
void pWidget::restart()
|
|
{
|
|
statusBar()->clear();
|
|
dill->startNew();
|
|
}
|
|
|
|
void pWidget::setGameCaption()
|
|
{
|
|
TQString name = games->currentText();
|
|
TQString newname;
|
|
TQString gamenum;
|
|
gamenum.setNum( dill->gameNumber() );
|
|
for (uint i = 0; i < name.length(); i++)
|
|
if (name.at(i) != TQChar('&'))
|
|
newname += name.at(i);
|
|
|
|
setCaption( newname + " - " + gamenum );
|
|
}
|
|
|
|
void pWidget::newGameType()
|
|
{
|
|
delete dill;
|
|
dill = 0;
|
|
slotUpdateMoves();
|
|
|
|
uint id = games->currentItem();
|
|
for (TQValueList<DealerInfo*>::ConstIterator it = DealerInfoList::self()->games().begin(); it != DealerInfoList::self()->games().end(); ++it) {
|
|
if ((*it)->gameindex == id) {
|
|
dill = (*it)->createGame(this);
|
|
TQString name = (*it)->name;
|
|
name = name.replace(TQRegExp("[&']"), "");
|
|
name = name.replace(TQRegExp("[ ]"), "_").lower();
|
|
dill->setAnchorName("game_" + name);
|
|
connect(dill, TQT_SIGNAL(saveGame()), TQT_SLOT(saveGame()));
|
|
connect(dill, TQT_SIGNAL(gameInfo(const TQString&)),
|
|
TQT_SLOT(slotGameInfo(const TQString &)));
|
|
connect(dill, TQT_SIGNAL(updateMoves()),
|
|
TQT_SLOT(slotUpdateMoves()));
|
|
dill->setGameId(id);
|
|
dill->setupActions();
|
|
dill->setBackgroundPixmap(background, midcolor);
|
|
dill->startNew();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!dill) {
|
|
kdError() << "unimplemented game type " << id << endl;
|
|
dill = DealerInfoList::self()->games().first()->createGame(this);
|
|
}
|
|
|
|
connect(dill, TQT_SIGNAL(undoPossible(bool)), TQT_SLOT(undoPossible(bool)));
|
|
connect(dill, TQT_SIGNAL(gameWon(bool)), TQT_SLOT(gameWon(bool)));
|
|
connect(dill, TQT_SIGNAL(gameLost()), TQT_SLOT(gameLost()));
|
|
|
|
dill->setAutoDropEnabled(dropaction->isChecked());
|
|
|
|
// it's a bit tricky - we have to do this here as the
|
|
// base class constructor runs before the derived class's
|
|
dill->takeState();
|
|
|
|
setGameCaption();
|
|
|
|
TDEConfig *config = kapp->config();
|
|
TDEConfigGroupSaver kcs(config, settings_group);
|
|
config->writeEntry("DefaultGame", id);
|
|
|
|
TQSize min(700,400);
|
|
min = min.expandedTo(dill->minimumCardSize());
|
|
dill->setMinimumSize(min);
|
|
dill->resize(min);
|
|
updateGeometry();
|
|
setCentralWidget(dill);
|
|
dill->show();
|
|
}
|
|
|
|
void pWidget::showEvent(TQShowEvent *e)
|
|
{
|
|
if (dill)
|
|
dill->setMinimumSize(TQSize(0,0));
|
|
KMainWindow::showEvent(e);
|
|
}
|
|
|
|
void pWidget::slotGameInfo(const TQString &text)
|
|
{
|
|
statusBar()->message(text, 3000);
|
|
}
|
|
|
|
void pWidget::slotUpdateMoves()
|
|
{
|
|
int moves = 0;
|
|
if ( dill ) moves = dill->getMoves();
|
|
statusBar()->changeItem( i18n("1 move", "%n moves", moves), 1 );
|
|
}
|
|
|
|
void pWidget::setBackSide(const TQString &deck, const TQString &cards)
|
|
{
|
|
TDEConfig *config = kapp->config();
|
|
TDEConfigGroupSaver kcs(config, settings_group);
|
|
TQPixmap pm(deck);
|
|
if(!pm.isNull()) {
|
|
cardMap::self()->setBackSide(pm, false);
|
|
config->writeEntry("Back", deck);
|
|
bool ret = cardMap::self()->setCardDir(cards);
|
|
if (!ret) {
|
|
config->writeEntry("Back", "");
|
|
|
|
}
|
|
config->writeEntry("Cards", cards);
|
|
cardMap::self()->setBackSide(pm, true);
|
|
} else
|
|
KMessageBox::sorry(this,
|
|
i18n("Could not load background image!"));
|
|
|
|
if (dill) {
|
|
dill->canvas()->setAllChanged();
|
|
dill->canvas()->update();
|
|
}
|
|
}
|
|
|
|
void pWidget::chooseGame()
|
|
{
|
|
bool ok;
|
|
long number = KInputDialog::getText(i18n("Game Number"), i18n("Enter a game number (FreeCell deals are the same as in the FreeCell FAQ):"), TQString::number(dill->gameNumber()), 0, this).toLong(&ok);
|
|
if (ok) {
|
|
dill->setGameNumber(number);
|
|
setGameCaption();
|
|
restart();
|
|
}
|
|
}
|
|
|
|
void pWidget::gameWon(bool withhelp)
|
|
{
|
|
TQString congrats;
|
|
if (withhelp)
|
|
congrats = i18n("Congratulations! We have won!");
|
|
else
|
|
congrats = i18n("Congratulations! You have won!");
|
|
#if TEST_SOLVER == 0
|
|
KMessageBox::information(this, congrats, i18n("Congratulations!"));
|
|
#endif
|
|
TQTimer::singleShot(0, TQT_TQOBJECT(this), TQT_SLOT(newGame()));
|
|
#if TEST_SOLVER == 1
|
|
dill->demo();
|
|
#endif
|
|
}
|
|
|
|
void pWidget::gameLost()
|
|
{
|
|
TQString dontAskAgainName = "gameLostDontAskAgain";
|
|
|
|
// The following code is taken out of kmessagebox.cpp in tdeui.
|
|
// Is there a better way?
|
|
TDEConfig *config = 0;
|
|
TQString grpNotifMsgs = TQString::fromLatin1("Notification Messages");
|
|
|
|
config = TDEGlobal::config();
|
|
TDEConfigGroupSaver saver(config,
|
|
TQString::fromLatin1("Notification Messages"));
|
|
TQString dontAsk = config->readEntry(dontAskAgainName).lower();
|
|
|
|
// If we are ordered never to ask again and to continue the game,
|
|
// then do so.
|
|
if (dontAsk == "no")
|
|
return;
|
|
// If it says yes, we ask anyway. Just starting a new game would
|
|
// be incredibly annoying.
|
|
if (dontAsk == "yes")
|
|
dontAskAgainName = TQString();
|
|
|
|
if (KMessageBox::questionYesNo(this, i18n("You could not win this game, "
|
|
"but there is always a second try.\nStart a new game?"),
|
|
i18n("Could Not Win!"),
|
|
i18n("New Game"),
|
|
KStdGuiItem::cont(),
|
|
dontAskAgainName) == KMessageBox::Yes) {
|
|
|
|
TQTimer::singleShot(0, TQT_TQOBJECT(this), TQT_SLOT(newGame()));
|
|
}
|
|
}
|
|
|
|
void pWidget::openGame(const KURL &url)
|
|
{
|
|
TQString tmpFile;
|
|
if( TDEIO::NetAccess::download( url, tmpFile, this ) )
|
|
{
|
|
TQFile of(tmpFile);
|
|
of.open(IO_ReadOnly);
|
|
TQDomDocument doc;
|
|
TQString error;
|
|
if (!doc.setContent(&of, &error))
|
|
{
|
|
KMessageBox::sorry(this, error);
|
|
return;
|
|
}
|
|
uint id = doc.documentElement().attribute("id").toUInt();
|
|
|
|
if (id != (TQ_UINT32)games->currentItem()) {
|
|
games->setCurrentItem(id);
|
|
newGameType();
|
|
if (!dill) {
|
|
KMessageBox::error(this, i18n("The saved game is of unknown type!"));
|
|
games->setCurrentItem(0);
|
|
newGameType();
|
|
}
|
|
}
|
|
dill->openGame(doc);
|
|
setGameCaption();
|
|
TDEIO::NetAccess::removeTempFile( tmpFile );
|
|
recent->addURL(url);
|
|
recent->saveEntries(TDEGlobal::config());
|
|
}
|
|
}
|
|
|
|
void pWidget::openGame()
|
|
{
|
|
KURL url = KFileDialog::getOpenURL();
|
|
openGame(url);
|
|
}
|
|
|
|
void pWidget::saveGame()
|
|
{
|
|
KURL url = KFileDialog::getSaveURL();
|
|
KTempFile file;
|
|
TQDomDocument doc("kpat");
|
|
dill->saveGame(doc);
|
|
TQTextStream *stream = file.textStream();
|
|
*stream << doc.toString();
|
|
file.close();
|
|
TDEIO::NetAccess::upload(file.name(), url, this);
|
|
recent->addURL(url);
|
|
recent->saveEntries(TDEGlobal::config());
|
|
}
|
|
|
|
void pWidget::showStats()
|
|
{
|
|
GameStatsImpl* dlg = new GameStatsImpl(this,"statistics dialog");
|
|
if (dill)
|
|
dlg->showGameType(dill->gameId());
|
|
dlg->exec();
|
|
}
|
|
|
|
#include "pwidget.moc"
|
|
|