/* 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 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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(KGlobal::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::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); KGlobal::dirs()->addResourceType("wallpaper", KStandardDirs::kde_default("data") + "kpat/backgrounds/"); KGlobal::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 = KGlobal::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")); KConfig *config = kapp->config(); KConfigGroupSaver 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() { KConfig *config = kapp->config(); KConfigGroupSaver 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("Couldn't load wallpaper
%1
").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) { KConfig *config = kapp->config(); KConfigGroupSaver 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(); KConfig *config = kapp->config(); KConfigGroupSaver cs(config, settings_group ); config->writeEntry( "Animation", anim); } void pWidget::enableAutoDrop() { bool drop = dropaction->isChecked(); KConfig *config = kapp->config(); KConfigGroupSaver 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::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(); KConfig *config = kapp->config(); KConfigGroupSaver 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) { KConfig *config = kapp->config(); KConfigGroupSaver 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? KConfig *config = 0; TQString grpNotifMsgs = TQString::fromLatin1("Notification Messages"); config = KGlobal::config(); KConfigGroupSaver 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( KIO::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(); KIO::NetAccess::removeTempFile( tmpFile ); recent->addURL(url); recent->saveEntries(KGlobal::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(); KIO::NetAccess::upload(file.name(), url, this); recent->addURL(url); recent->saveEntries(KGlobal::config()); } void pWidget::showStats() { GameStatsImpl* dlg = new GameStatsImpl(this,"statistics dialog"); if (dill) dlg->showGameType(dill->gameId()); dlg->exec(); } #include "pwidget.moc"