summaryrefslogtreecommitdiffstats
path: root/kbackgammon/engines
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitc90c389a8a8d9d8661e9772ec4144c5cf2039f23 (patch)
tree6d8391395bce9eaea4ad78958617edb20c6a7573 /kbackgammon/engines
downloadtdegames-c90c389a8a8d9d8661e9772ec4144c5cf2039f23.tar.gz
tdegames-c90c389a8a8d9d8661e9772ec4144c5cf2039f23.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegames@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kbackgammon/engines')
-rw-r--r--kbackgammon/engines/Makefile.am16
-rw-r--r--kbackgammon/engines/fibs/Makefile.am9
-rw-r--r--kbackgammon/engines/fibs/clip.h39
-rw-r--r--kbackgammon/engines/fibs/kbgfibs.cpp2314
-rw-r--r--kbackgammon/engines/fibs/kbgfibs.h479
-rw-r--r--kbackgammon/engines/fibs/kbgfibschat.cpp828
-rw-r--r--kbackgammon/engines/fibs/kbgfibschat.h273
-rw-r--r--kbackgammon/engines/fibs/kbginvite.cpp185
-rw-r--r--kbackgammon/engines/fibs/kbginvite.h113
-rw-r--r--kbackgammon/engines/fibs/kplayerlist.cpp902
-rw-r--r--kbackgammon/engines/fibs/kplayerlist.h298
-rw-r--r--kbackgammon/engines/generic/Makefile.am8
-rw-r--r--kbackgammon/engines/generic/kbgengine.cpp62
-rw-r--r--kbackgammon/engines/generic/kbgengine.h298
-rw-r--r--kbackgammon/engines/gnubg/Makefile.am9
-rw-r--r--kbackgammon/engines/gnubg/kbggnubg.cpp710
-rw-r--r--kbackgammon/engines/gnubg/kbggnubg.h223
-rw-r--r--kbackgammon/engines/nextgen/Makefile.am9
-rw-r--r--kbackgammon/engines/nextgen/kbggame.cpp47
-rw-r--r--kbackgammon/engines/nextgen/kbggame.h57
-rw-r--r--kbackgammon/engines/nextgen/kbgng.cpp622
-rw-r--r--kbackgammon/engines/nextgen/kbgng.h263
-rw-r--r--kbackgammon/engines/nextgen/kbgplayer.cpp62
-rw-r--r--kbackgammon/engines/nextgen/kbgplayer.h58
-rw-r--r--kbackgammon/engines/offline/Makefile.am9
-rw-r--r--kbackgammon/engines/offline/kbgoffline.cpp810
-rw-r--r--kbackgammon/engines/offline/kbgoffline.h213
27 files changed, 8916 insertions, 0 deletions
diff --git a/kbackgammon/engines/Makefile.am b/kbackgammon/engines/Makefile.am
new file mode 100644
index 00000000..e599d9b2
--- /dev/null
+++ b/kbackgammon/engines/Makefile.am
@@ -0,0 +1,16 @@
+noinst_LTLIBRARIES = libkbgengines.la
+
+libkbgengines_la_SOURCES = dummy.cpp
+libkbgengines_la_LIBADD = offline/libkbgoffline.la gnubg/libkbggnubg.la \
+ generic/libkbggeneric.la fibs/libkbgfibs.la \
+ nextgen/libkbgnextgen.la
+
+INCLUDES= $(all_includes)
+
+METASOURCES = AUTO
+
+SUBDIRS = offline generic fibs gnubg nextgen
+
+dummy.cpp:
+ echo > dummy.cpp
+
diff --git a/kbackgammon/engines/fibs/Makefile.am b/kbackgammon/engines/fibs/Makefile.am
new file mode 100644
index 00000000..e32522de
--- /dev/null
+++ b/kbackgammon/engines/fibs/Makefile.am
@@ -0,0 +1,9 @@
+noinst_LTLIBRARIES = libkbgfibs.la
+
+libkbgfibs_la_SOURCES = kbgfibs.cpp kplayerlist.cpp kbginvite.cpp kbgfibschat.cpp
+
+INCLUDES= -I$(top_srcdir)/kbackgammon/engines -I$(top_srcdir)/libkdegames \
+ -I$(top_srcdir)/kbackgammon $(all_includes)
+
+METASOURCES = AUTO
+
diff --git a/kbackgammon/engines/fibs/clip.h b/kbackgammon/engines/fibs/clip.h
new file mode 100644
index 00000000..e016eb5b
--- /dev/null
+++ b/kbackgammon/engines/fibs/clip.h
@@ -0,0 +1,39 @@
+/*
+
+ This file defines constants of the "CLIent Protocol" of FIBS.
+ It comes directly from Marvin and I guess it is copyrighted
+ by him. If you have questions regarding this file, try to
+ visit
+
+ http://fibs.demon.co.uk/clip.html
+
+*/
+
+#ifndef KFIBS_CLIP_H
+#define KFIBS_CLIP_H
+
+
+#define CLIP_VERSION 1008
+
+#define CLIP_WELCOME 1
+#define CLIP_OWN_INFO 2
+#define CLIP_MOTD_BEGIN 3
+#define CLIP_MOTD_END 4
+#define CLIP_WHO_INFO 5
+#define CLIP_WHO_END 6
+#define CLIP_LOGIN 7
+#define CLIP_LOGOUT 8
+#define CLIP_MESSAGE 9
+#define CLIP_MESSAGE_DELIVERED 10
+#define CLIP_MESSAGE_SAVED 11
+#define CLIP_SAYS 12
+#define CLIP_SHOUTS 13
+#define CLIP_WHISPERS 14
+#define CLIP_KIBITZES 15
+#define CLIP_YOU_SAY 16
+#define CLIP_YOU_SHOUT 17
+#define CLIP_YOU_WHISPER 18
+#define CLIP_YOU_KIBITZ 19
+
+
+#endif // KFIBS_CLIP_H
diff --git a/kbackgammon/engines/fibs/kbgfibs.cpp b/kbackgammon/engines/fibs/kbgfibs.cpp
new file mode 100644
index 00000000..06fdaec7
--- /dev/null
+++ b/kbackgammon/engines/fibs/kbgfibs.cpp
@@ -0,0 +1,2314 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+/*
+
+ TODO: popup dialog for accept/reject and join ??
+ clear the chat history?
+ game over, clear the caption?
+ need show saved
+ need buddy list
+ need wait for player,...
+
+*/
+
+#include "kbgfibs.h"
+#include "kbgfibs.moc"
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <qtimer.h>
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <qstring.h>
+#include <qsocket.h>
+#include <qpopupmenu.h>
+#include <qgroupbox.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <kmainwindow.h>
+#include <klineeditdlg.h>
+#include <kmessagebox.h>
+#include <qdatetime.h>
+#include <qwhatsthis.h>
+#include <kaudioplayer.h>
+#include <kstandarddirs.h>
+#include <qvbox.h>
+#include <kiconloader.h>
+#include <ktabctl.h>
+#include <kpassdlg.h>
+#include <qcstring.h>
+#include <knotifyclient.h>
+#include <kaction.h>
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <iostream>
+#include "kbgboard.h"
+#include "kbgstatus.h"
+
+#include "clip.h"
+#include "version.h"
+
+
+void KBgEngineFIBS::start()
+{
+ // FIXME: open the child windows here and not in the constructor
+}
+
+// == configuration handling ===================================================
+
+/*
+ * Restore settings and ask children to do the same
+ */
+void KBgEngineFIBS::readConfig()
+{
+ KConfig *config = kapp->config();
+ config->setGroup("fibs engine");
+
+ // history variables
+ lastAway = config->readEntry("away_hist", "");
+
+ // various options
+ showMsg = config->readBoolEntry("pers_msg", false);
+ whoisInvite = config->readBoolEntry("whois_invite", false);
+
+ // connection information
+ infoFIBS[FIBSHost] = config->readEntry("server", "fibs.com");
+ infoFIBS[FIBSPort] = config->readEntry("port", "4321");
+ infoFIBS[FIBSUser] = config->readEntry("user", "");
+ infoFIBS[FIBSPswd] = config->readEntry("password", "");
+
+ // automatic messages
+ useAutoMsg[MsgBeg] = config->readBoolEntry("auto-beg", false);
+ useAutoMsg[MsgLos] = config->readBoolEntry("auto-los", false);
+ useAutoMsg[MsgWin] = config->readBoolEntry("auto-win", false);
+
+ autoMsg[MsgBeg] = config->readEntry("msg-beg", "");
+ autoMsg[MsgLos] = config->readEntry("msg-los", "");
+ autoMsg[MsgWin] = config->readEntry("msg-win", "");
+
+ // ask the children to read their config options
+ playerlist->readConfig();
+ chatWindow->readConfig();
+}
+
+/*
+ * Save the engine specific settings and tell all clients
+ */
+void KBgEngineFIBS::saveConfig()
+{
+ KConfig *config = kapp->config();
+ config->setGroup("fibs engine");
+
+ // history variables
+ config->writeEntry("away_hist", lastAway);
+
+ // various options
+ config->writeEntry("pers_msg", showMsg);
+ config->writeEntry("whois_invite", whoisInvite);
+
+ // connection information
+ config->writeEntry("server", infoFIBS[FIBSHost]);
+ config->writeEntry("port", infoFIBS[FIBSPort]);
+ config->writeEntry("user", infoFIBS[FIBSUser]);
+ config->writeEntry("password", infoFIBS[FIBSPswd]);
+
+ // automatic messages
+ config->writeEntry("auto-beg", useAutoMsg[MsgBeg]);
+ config->writeEntry("auto-los", useAutoMsg[MsgLos]);
+ config->writeEntry("auto-win", useAutoMsg[MsgWin]);
+
+ config->writeEntry("msg-beg", autoMsg[MsgBeg]);
+ config->writeEntry("msg-los", autoMsg[MsgLos]);
+ config->writeEntry("msg-win", autoMsg[MsgWin]);
+
+ // ask the children to read their config options
+ playerlist->saveConfig();
+ chatWindow->saveConfig();
+}
+
+void KBgEngineFIBS::setupDefault()
+{
+
+ cbp->setChecked(false);
+ cbi->setChecked(false);
+
+ lec[FIBSHost]->setText("fibs.com");
+ lec[FIBSPort]->setText("4321");
+
+ lec[FIBSUser]->clear();
+ lec[FIBSPswd]->clear();
+
+
+ chatWindow->setupDefault();
+ playerlist->setupDefault();
+}
+
+void KBgEngineFIBS::setupCancel()
+{
+ chatWindow->setupCancel();
+ playerlist->setupCancel();
+}
+
+/*
+ * Called when the setup dialog is positively closed
+ */
+void KBgEngineFIBS::setupOk()
+{
+ // various options
+ showMsg = cbp->isChecked();
+ whoisInvite = cbi->isChecked();
+
+ // connection information
+ for (int i = 0; i < NumFIBS; i++)
+ infoFIBS[i] = lec[i]->text();
+
+ // automatic messages
+ for (int i = 0; i < NumMsg; i++) {
+ useAutoMsg[i] = cbm[i]->isChecked();
+ autoMsg[i] = lem[i]->text();
+ }
+
+ chatWindow->setupOk();
+ playerlist->setupOk();
+
+ // save settings
+ saveConfig();
+}
+
+/*
+ * Puts the FIBS specific setup into the dialog nb
+ */
+void KBgEngineFIBS::getSetupPages(KDialogBase *nb)
+{
+ /*
+ * Main Widget
+ */
+ QVBox *vbp = nb->addVBoxPage(i18n("FIBS Engine"), i18n("Here you can configure the FIBS backgammon engine"),
+ kapp->iconLoader()->loadIcon(PROG_NAME "_engine", KIcon::Desktop));
+
+ /*
+ * Get a multi page work space
+ */
+ KTabCtl *tc = new KTabCtl(vbp, "fibs tabs");
+
+ /*
+ * FIBS, local options
+ */
+ QWidget *w = new QWidget(tc);
+ QGridLayout *gl = new QGridLayout(w, 3, 1, nb->spacingHint());
+
+ /*
+ * Group boxes
+ */
+ QGroupBox *gbo = new QGroupBox(i18n("Options"), w);
+ QGroupBox *gbm = new QGroupBox(i18n("Automatic Messages"), w);
+
+ gl->addWidget(gbo, 0, 0);
+ gl->addWidget(gbm, 1, 0);
+
+ /*
+ * Options
+ */
+ cbp = new QCheckBox(i18n("Show copy of personal messages in main window"), gbo);
+ cbi = new QCheckBox(i18n("Automatically request player info on invitation"), gbo);
+
+ QWhatsThis::add(cbp, i18n("Usually, all messages sent directly to you by other players "
+ "are displayed only in the chat window. Check this box if you "
+ "would like to get a copy of these messages in the main window."));
+ QWhatsThis::add(cbi, i18n("Check this box if you would like to receive information on "
+ "players that invite you to games."));
+
+ cbp->setChecked(showMsg);
+ cbi->setChecked(whoisInvite);
+
+ gl = new QGridLayout(gbo, 2, 1, 20);
+ gl->addWidget(cbp, 0, 0);
+ gl->addWidget(cbi, 1, 0);
+
+ /*
+ * Automatic messages
+ */
+ gl = new QGridLayout(gbm, NumMsg, 2, 20);
+
+ cbm[MsgBeg] = new QCheckBox(i18n("Start match:"), gbm);
+ cbm[MsgWin] = new QCheckBox(i18n("Win match:"), gbm);
+ cbm[MsgLos] = new QCheckBox(i18n("Lose match:"), gbm);
+
+ QWhatsThis::add(cbm[MsgBeg], i18n("If you want to send a standard greeting to your "
+ "opponent whenever you start a new match, check "
+ "this box and write the message into the entry "
+ "field."));
+ QWhatsThis::add(cbm[MsgWin], i18n("If you want to send a standard message to your "
+ "opponent whenever you won a match, check this box "
+ "and write the message into the entry field."));
+ QWhatsThis::add(cbm[MsgLos], i18n("If you want to send a standard message to your "
+ "opponent whenever you lost a match, check this box "
+ "and write the message into the entry field."));
+
+ for (int i = 0; i < NumMsg; i++) {
+ lem[i] = new QLineEdit(autoMsg[i], gbm);
+ gl->addWidget(cbm[i], i, 0);
+ gl->addWidget(lem[i], i, 1);
+ connect(cbm[i], SIGNAL(toggled(bool)), lem[i], SLOT(setEnabled(bool)));
+ cbm[i]->setChecked(useAutoMsg[i]);
+ lem[i]->setEnabled(useAutoMsg[i]);
+ QWhatsThis::add(lem[i], QWhatsThis::textFor(cbm[i]));
+ }
+
+ /*
+ * Put the page into the notebook
+ */
+ gl->activate();
+ tc->addTab(w, i18n("&Local"));
+
+
+ /*
+ * FIBS, connection setup
+ */
+ w = new QWidget(tc);
+ gl = new QGridLayout(w, 3, 1, nb->spacingHint());
+
+ QGroupBox *gbc = new QGroupBox(i18n("Server"), w);
+ QGroupBox *gbk = new QGroupBox(i18n("Other"), w);
+
+ gl->addWidget(gbc, 0, 0);
+ gl->addWidget(gbk, 1, 0);
+
+ /*
+ * Server box
+ */
+ gl = new QGridLayout(gbc, 4, 2, 20);
+
+ QLabel *lbc[NumFIBS];
+
+ lbc[FIBSHost] = new QLabel(i18n("Server name:"), gbc);
+ lbc[FIBSPort] = new QLabel(i18n("Server port:"), gbc);
+ lbc[FIBSUser] = new QLabel(i18n("User name:"), gbc);
+ lbc[FIBSPswd] = new QLabel(i18n("Password:"), gbc);
+
+ for (int i = 0; i < NumFIBS; i++) {
+ lec[i] = new QLineEdit(infoFIBS[i], gbc);
+ gl->addWidget(lbc[i], i, 0);
+ gl->addWidget(lec[i], i, 1);
+ }
+ lec[FIBSPswd]->setEchoMode(QLineEdit::Password);
+
+ QWhatsThis::add(lec[FIBSHost], i18n("Enter here the host name of FIBS. With almost "
+ "absolute certainty this should be \"fibs.com\". "
+ "If you leave this blank, you will be asked again "
+ "at connection time."));
+ QWhatsThis::add(lec[FIBSPort], i18n("Enter here the port number of FIBS. With almost "
+ "absolute certainty this should be \"4321\". "
+ "If you leave this blank, you will be asked again "
+ "at connection time."));
+ QWhatsThis::add(lec[FIBSUser], i18n("Enter your login on FIBS here. If you do not have a "
+ "login yet, you should first create an account using "
+ "the corresponding menu entry. If you leave this blank, "
+ "you will be asked again at connection time."));
+ QWhatsThis::add(lec[FIBSPswd], i18n("Enter your password on FIBS here. If you do not have a "
+ "login yet, you should first create an account using "
+ "the corresponding menu entry. If you leave this blank, "
+ "you will be asked again at connection time. The password "
+ "will not be visible."));
+
+ /*
+ * Connection keepalive
+ */
+ cbk = new QCheckBox(i18n("Keep connections alive"), gbk);
+
+ QWhatsThis::add(cbk, i18n("Usually, FIBS drops the connection after one hour of inactivity. When "
+ "you check this box, %1 will try to keep the connection alive, even "
+ "if you are not actually playing or chatting. Use this with caution "
+ "if you do not have flat-rate Internet access.").arg(PROG_NAME));
+
+ cbk->setChecked(keepalive);
+
+ gl = new QGridLayout(gbk, 1, 1, nb->spacingHint());
+ gl->addWidget(cbk, 0, 0);
+
+ /*
+ * Done with the page, put it in
+ */
+ gl->activate();
+ tc->addTab(w, i18n("&Connection"));
+
+ /*
+ * Ask children for settings
+ */
+ chatWindow->getSetupPages(tc, nb->spacingHint());
+ playerlist->getSetupPages(tc, nb->spacingHint());
+
+ /*
+ * TODO: future extensions
+ */
+ w = new QWidget(tc);
+ tc->addTab(w, i18n("&Buddy List"));
+}
+
+
+// == functions related to the invitation menu =================================
+
+/*
+ * Remove a player from the invitation list in the join menu
+ */
+void KBgEngineFIBS::cancelJoin(const QString &info)
+{
+ QRegExp patt = QRegExp("^" + info + " ");
+
+ for (int i = 0; i <= numJoin; i++) {
+ if (actJoin[i]->text().contains(patt)) {
+ // move all entries starting at i+1 up by one...
+ for (int j = i; j < numJoin; j++)
+ actJoin[j]->setText(actJoin[j+1]->text());
+ actJoin[numJoin--]->unplug(joinMenu);
+ break;
+ }
+ }
+}
+
+/*
+ * Parse the information in info for the purposes of the invitation
+ * submenu
+ */
+void KBgEngineFIBS::changeJoin(const QString &info)
+{
+ char name_p[100], name_o[100];
+ float rate;
+ int expi;
+
+ /*
+ * Extract the name of the player, her opponent, rating and experience.
+ * It is okay to use latin1(), since the string is coming from FIBS.
+ */
+ sscanf(info.latin1(), "%99s %99s %*s %*s %*s %f %i %*s %*s %*s %*s %*s",
+ name_p, name_o, &rate, &expi);
+
+ QString name = name_p;
+ QString oppo = name_o;
+
+ QString rate_s; rate_s.setNum(rate);
+ QString expi_s; expi_s.setNum(expi);
+
+ QRegExp patt = QRegExp("^" + name + " ");
+
+ /*
+ * We have essentially two lists of names to check against: the ones
+ * that have invited us and are not yet in the menu and the ones that
+ * are already in the menu.
+ */
+
+ if (numJoin > -1 && oppo != "-")
+ cancelJoin(name);
+
+ for (QStringList::Iterator it = invitations.begin(); it != invitations.end(); ++it) {
+
+ if ((*it).contains(patt)) {
+
+ QString text, menu;
+
+ if ((*it).contains(QRegExp(" r$"))) {
+ menu = i18n("R means resume", "%1 (R)").arg(name);
+ text = i18n("%1 (experience %2, rating %3) wants to resume a saved match with you. "
+ "If you want to play, use the corresponding menu entry to join (or type "
+ "'join %4').").arg(name).arg(expi_s).arg(rate_s).arg(name);
+ KNotifyClient::event("invitation", i18n("%1 wants to resume a saved match with you").
+ arg(name));
+ } else if ((*it).contains(QRegExp(" u$"))) {
+ menu = i18n("U means unlimited", "%1 (U)").arg(name);
+ text = i18n("%1 (experience %2, rating %3) wants to play an unlimited match with you. "
+ "If you want to play, use the corresponding menu entry to join (or type "
+ "'join %4').").arg(name).arg(expi_s).arg(rate_s).arg(name);
+ KNotifyClient::event("invitation", i18n("%1 has invited you to an unlimited match").
+ arg(name));
+ } else {
+ QString len = (*it).right((*it).length() - name.length() - 1);
+ menu = i18n("If the format of the (U) and (R) strings is changed, it should also be changed here",
+ "%1 (%2)").arg(name).arg(len);
+ text = i18n("%1 (experience %2, rating %3) wants to play a %4 point match with you. "
+ "If you want to play, use the corresponding menu entry to join (or type "
+ "'join %5').").arg(name).arg(expi_s).arg(rate_s).arg(len).arg(name);
+ KNotifyClient::event("invitation", i18n("%1 has invited you for a %2 point match").
+ arg(name).arg(len));
+ }
+ emit serverString("rawwho " + name); // this avoids a race
+ if (whoisInvite) {
+ emit serverString("whois " + name);
+ emit infoText("<font color=\"red\">" + text + "</font>");
+ } else
+ emit infoText("<font color=\"red\">" + text + "</font><br>");
+
+ for (int i = 0; i <=numJoin; i++)
+ actJoin[i]->unplug(joinMenu);
+
+ if (++numJoin > 7) numJoin = 7;
+
+ for (int i = numJoin; i > 0; i--)
+ actJoin[i]->setText(actJoin[i-1]->text());
+
+ actJoin[0]->setText(menu);
+
+ for (int i = 0; i <= numJoin; i++)
+ actJoin[i]->plug(joinMenu);
+
+ invitations.remove(it);
+ break;
+ }
+ }
+
+ /*
+ * If there are entries in the menu, enable it
+ */
+ menu->setItemEnabled(joinMenuID, numJoin > -1);
+}
+
+
+// == various slots and functions ==============================================
+
+/*
+ * Keep the connection alive.
+ */
+void KBgEngineFIBS::keepAlive()
+{
+ emit serverString("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+}
+
+/*
+ * Several bookkeeping operations that have to be done at the
+ * end of every game. Some of these may or may not be necessary
+ * at a particular time, but they don't hurt either.
+ */
+void KBgEngineFIBS::endGame()
+{
+ playing = false;
+
+ emit serverString("rawwho " + infoFIBS[FIBSUser]);
+
+ actConti->setEnabled(false);
+ actLeave->setEnabled(false);
+
+ actAccept->setEnabled(false);
+ actReject->setEnabled(false);
+
+ emit allowCommand(Load, false);
+ emit allowCommand(Undo, false);
+ emit allowCommand(Done, false);
+ emit allowCommand(Cube, false);
+ emit allowCommand(Roll, false);
+}
+
+/*
+ * Toggle visibility of the player list
+ */
+void KBgEngineFIBS::showList()
+{
+ playerlist->isVisible() ? playerlist->hide() : playerlist->show();
+}
+
+/*
+ * Toggle visibility of the chat window
+ */
+void KBgEngineFIBS::showChat()
+{
+ chatWindow->isVisible() ? chatWindow->hide() : chatWindow->show();
+}
+
+/*
+ * Process the last move coming from the board
+ */
+void KBgEngineFIBS::handleMove(QString *s)
+{
+ lastMove = *s;
+ QString t = lastMove.left(1);
+ int moves = t.toInt();
+
+ emit allowCommand(Done, moves == toMove);
+ emit allowCommand(Undo, moves > 0);
+
+ /*
+ * Allow undo and possibly start the commit timer
+ */
+ redoPossible &= ((moves < toMove) && (undoCounter > 0));
+ emit allowCommand(Redo, redoPossible);
+ if (moves == toMove && cl >= 0) {
+ emit allowMoving(false);
+ ct->start(cl, true);
+ }
+}
+
+/*
+ * Done with the move
+ */
+void KBgEngineFIBS::done()
+{
+ // prevent the timer from expiring again
+ ct->stop();
+
+ // no more moves
+ emit allowMoving(false);
+
+ // no more commands until it's our turn
+ emit allowCommand(Load, false);
+ emit allowCommand(Undo, false);
+ emit allowCommand(Done, false);
+ emit allowCommand(Cube, false);
+ emit allowCommand(Roll, false);
+
+ // Transform the string to FIBS cormat
+ lastMove.replace(0, 2, "move ");
+ lastMove.replace(pat[PlsChar], "-");
+
+ // sent it to the server
+ emit serverString(lastMove);
+}
+
+/*
+ * Undo the last move
+ */
+void KBgEngineFIBS::undo()
+{
+ ct->stop();
+
+ redoPossible = true;
+ ++undoCounter;
+
+ emit allowMoving(true);
+ emit allowCommand(Done, false);
+ emit allowCommand(Redo, true);
+ emit undoMove();
+}
+
+/*
+ * Redo the last undone move
+ */
+void KBgEngineFIBS::redo()
+{
+ --undoCounter;
+ emit redoMove();
+}
+
+/*
+ * Double the cube - coming from the board
+ */
+void KBgEngineFIBS::doubleCube(const int w)
+{
+ if (playing && w == US) cube();
+}
+
+/*
+ * Roll the dice - coming from the board
+ */
+void KBgEngineFIBS::rollDice(const int w)
+{
+ if (playing && w == US) roll();
+}
+
+/*
+ * This engine passes all commands unmodified to the server
+ */
+void KBgEngineFIBS::handleCommand(QString const &cmd)
+{
+ emit serverString(cmd);
+}
+
+/*
+ * If we have a connection, we don't quit right away
+ */
+bool KBgEngineFIBS::queryClose()
+{
+ if (connection->state() == QSocket::Idle)
+ return true;
+
+ switch (KMessageBox::warningYesNoCancel((QWidget *)parent(),i18n("Still connected. Log out first?"),QString::null,i18n("Log Out"), i18n("Stay Connected"))) {
+ case KMessageBox::Yes :
+ disconnectFIBS();
+ return true;
+ case KMessageBox::No :
+ return true;
+ default: // cancel
+ return false;
+ }
+}
+
+/*
+ * If we have a connection, we don't quit right away
+ */
+bool KBgEngineFIBS::queryExit()
+{
+ if( kapp->sessionSaving())
+ return true;
+ if (connection->state() != QSocket::Idle)
+ disconnectFIBS();
+ return true;
+}
+
+/*
+ * This displays a copy of personal messages in the main window.
+ * Normally, these only get displayed in the chat window.
+ */
+void KBgEngineFIBS::personalMessage(const QString &msg)
+{
+ if (showMsg)
+ emit infoText(msg);
+}
+
+
+// == slots and functions for FIBS commands ====================================
+
+/*
+ * Accept the offer
+ */
+void KBgEngineFIBS::accept()
+{
+ actAccept->setEnabled(false);
+ actReject->setEnabled(false);
+
+ emit serverString("accept");
+}
+
+/*
+ * Reject the offer
+ */
+void KBgEngineFIBS::reject()
+{
+ actAccept->setEnabled(false);
+ actReject->setEnabled(false);
+
+ emit serverString("reject");
+}
+
+/*
+ * Continue a multi game match
+ */
+void KBgEngineFIBS::match_conti()
+{
+ actConti->setEnabled(false);
+ actLeave->setEnabled(false);
+
+ emit serverString("join");
+}
+
+/*
+ * Leave a multi game match
+ */
+void KBgEngineFIBS::match_leave()
+{
+ actConti->setEnabled(false);
+ actLeave->setEnabled(false);
+
+ emit serverString("leave");
+}
+
+/*
+ * Go away from the server for a little while. Offer the last know away
+ * message as a default to the user.
+ */
+void KBgEngineFIBS::away()
+{
+ bool ret;
+ QString msg = KLineEditDlg::getText(i18n("Please type the message that should be displayed to other\n"
+ "users while you are away."),
+ lastAway, &ret, (QWidget *)parent());
+ if (ret) {
+ lastAway = msg;
+ emit serverString("away " + msg);
+ actAway->setEnabled(false);
+ }
+}
+
+/*
+ * Toggle being ready for games
+ */
+void KBgEngineFIBS::toggle_ready()
+{
+ emit serverString("toggle ready");
+}
+
+/*
+ * Toggle the use of greedy bearoffs
+ */
+void KBgEngineFIBS::toggle_greedy()
+{
+ emit serverString("toggle greedy");
+}
+
+/*
+ * Toggle whether we will be asked to double/roll or not
+ */
+void KBgEngineFIBS::toggle_double()
+{
+ emit serverString("toggle double");
+}
+
+/*
+ * Toggle whether we want to see details on rating computations
+ */
+void KBgEngineFIBS::toggle_ratings()
+{
+ emit serverString("toggle ratings");
+}
+
+/*
+ * Come back after being away.
+ */
+void KBgEngineFIBS::back()
+{
+ emit serverString("back");
+}
+
+/*
+ * Double the cube
+ */
+void KBgEngineFIBS::cube()
+{
+ emit serverString("double");
+}
+
+/*
+ * Roll the dice
+ */
+void KBgEngineFIBS::roll()
+{
+ emit serverString("roll");
+}
+
+/*
+ * Reload the board
+ */
+void KBgEngineFIBS::load()
+{
+ emit serverString("board");
+}
+
+/*
+ * Handle the menu short cuts for joining. This is not as pretty as it
+ * could or should be, but it works and is easy to understand.
+ */
+void KBgEngineFIBS::join(const QString &msg)
+{
+ emit serverString("join " + msg.left(msg.find('(')));
+}
+void KBgEngineFIBS::join_0() { join(actJoin[0]->text()); }
+void KBgEngineFIBS::join_1() { join(actJoin[1]->text()); }
+void KBgEngineFIBS::join_2() { join(actJoin[2]->text()); }
+void KBgEngineFIBS::join_3() { join(actJoin[3]->text()); }
+void KBgEngineFIBS::join_4() { join(actJoin[4]->text()); }
+void KBgEngineFIBS::join_5() { join(actJoin[5]->text()); }
+void KBgEngineFIBS::join_6() { join(actJoin[6]->text()); }
+void KBgEngineFIBS::join_7() { join(actJoin[7]->text()); }
+
+
+// == invitation handling ======================================================
+
+/*
+ * Show the invitation dialog and set the name to player
+ */
+void KBgEngineFIBS::inviteDialog()
+{
+ fibsRequestInvitation("");
+}
+
+/*
+ * Show the invitation dialog and set the name to player
+ */
+void KBgEngineFIBS::fibsRequestInvitation(const QString &player)
+{
+ if (!invitationDlg) {
+ QString p = player;
+ invitationDlg = new KBgInvite("invite");
+ connect(invitationDlg, SIGNAL(inviteCommand(const QString &)), this, SLOT(handleCommand(const QString &)));
+ connect(invitationDlg, SIGNAL(dialogDone()), this, SLOT(invitationDone()));
+ }
+ invitationDlg->setPlayer(player);
+ invitationDlg->show();
+}
+
+/*
+ * Finish off the invitation dialog
+ */
+void KBgEngineFIBS::invitationDone()
+{
+ delete invitationDlg;
+ invitationDlg = 0;
+}
+
+
+// == connection handling ======================================================
+
+/*
+ * Establish a connection to the server and log in if the parameter login
+ * is true.
+ */
+void KBgEngineFIBS::connectFIBS()
+{
+ /*
+ * Make sure the connection parameter are properly set.
+ */
+ if (!queryConnection(false))
+ return;
+
+ conAction->setEnabled(false);
+ newAction->setEnabled(false);
+ disAction->setEnabled(false);
+
+ /*
+ * Connect
+ */
+ emit infoText(i18n("Looking up %1").arg(infoFIBS[FIBSHost]));
+ connection->connectToHost(infoFIBS[FIBSHost], infoFIBS[FIBSPort].toUShort());
+
+ return;
+}
+
+/*
+ * Hostname has been resolved.
+ */
+void KBgEngineFIBS::hostFound()
+{
+ emit infoText(i18n("Connecting to %1").arg(infoFIBS[FIBSHost]));
+}
+
+/*
+ * An error has occurred. Reset and inform the user.
+ */
+void KBgEngineFIBS::connError(int f)
+{
+ switch (f) {
+ case QSocket::ErrConnectionRefused:
+ emit infoText(i18n("Error, connection has been refused"));
+ break;
+ case QSocket::ErrHostNotFound:
+ emit infoText(i18n("Error, nonexistent host or name server down."));
+ break;
+ case QSocket::ErrSocketRead:
+ emit infoText(i18n("Error, reading data from socket"));
+ break;
+ }
+ connectionClosed();
+ return;
+}
+
+void KBgEngineFIBS::readData()
+{
+ QString line;
+ while(connection->canReadLine()) {
+ line = connection->readLine();
+ if (line.length() > 2) {
+ line.truncate(line.length()-2);
+ handleServerData(line);
+ }
+ }
+}
+
+/*
+ * Transmit the string s to the server
+ */
+void KBgEngineFIBS::sendData(const QString &s)
+{
+ connection->writeBlock((s+"\r\n").latin1(),2+s.length());
+}
+
+/*
+ * Connection has been established. Log in and update the menus & actions.
+ */
+void KBgEngineFIBS::connected()
+{
+ conAction->setEnabled(false);
+ newAction->setEnabled(false);
+ disAction->setEnabled(true);
+
+ menu->setItemEnabled( cmdMenuID, true);
+ menu->setItemEnabled(respMenuID, true);
+ menu->setItemEnabled(optsMenuID, true);
+
+ /*
+ * Initialize the rx state machine
+ */
+ rxStatus = RxConnect;
+ rxCollect = "";
+
+ /*
+ * Depending on whether somebody else wants to handle the login or not
+ */
+ if (login) {
+
+ /*
+ * Make sure the player list is empty when the whole list comes
+ * right after login
+ */
+ playerlist->clear();
+
+ /*
+ * Login, using the autologin feature of FIBS, before we even receive anything.
+ */
+ QString entry;
+ entry.setNum(CLIP_VERSION);
+ emit serverString(QString("login ") + PROG_NAME + "-" + PROG_VERSION + " " + entry + " "
+ + infoFIBS[FIBSUser] + " " + infoFIBS[FIBSPswd]);
+
+ } else {
+
+ emit serverString("guest");
+ login = true;
+
+ }
+
+ /*
+ * Some visual feedback and done
+ */
+ emit infoText(i18n("Connected") + "<br>");
+}
+
+/*
+ * Create a new account on FIBS. Obviously, this will also create
+ * a connection. The actual login is handled in the message parsing
+ * state machine.
+ */
+void KBgEngineFIBS::newAccount()
+{
+ if (!queryConnection(true))
+ return;
+
+ rxStatus = RxNewLogin;
+ rxCollect = "";
+ login = false;
+ connectFIBS();
+}
+
+/*
+ * Send a disconnection request to the server. The server will disconnect
+ * and we will receive a connectionClosed() signal.
+ */
+void KBgEngineFIBS::disconnectFIBS()
+{
+ // send two lines in case we are stuck in the login phase
+ emit serverString("quit");
+ emit serverString("quit");
+}
+
+/*
+ * Connection to the server is closed for some (unknown) reason. Delete
+ * the connection object and get the actions into a proper state.
+ */
+void KBgEngineFIBS::connectionClosed()
+{
+ /*
+ * Read remaining input
+ */
+ readData();
+
+ /*
+ * Flush whatever is left in the rxBuffer and send a note
+ */
+ emit infoText(rxCollect + "<br><hr>");
+ emit infoText(i18n("Disconnected.") + "<br>");
+
+ conAction->setEnabled(true);
+ newAction->setEnabled(true);
+ disAction->setEnabled(false);
+
+ menu->setItemEnabled(joinMenuID, false);
+ menu->setItemEnabled( cmdMenuID, false);
+ menu->setItemEnabled(respMenuID, false);
+ menu->setItemEnabled(optsMenuID, false);
+}
+
+/*
+ * To establish a connection, we need to query the server name, the port
+ * number, the login and the password.
+ */
+bool KBgEngineFIBS::queryConnection(const bool newlogin)
+{
+ QString text, msg;
+ bool first, ret = true;
+
+ /*
+ * query the connection parameter
+ */
+ if (newlogin || infoFIBS[FIBSHost].isEmpty()) {
+
+ msg = KLineEditDlg::getText(i18n("Enter the name of the server you want to connect to.\n"
+ "This should almost always be \"fibs.com\"."),
+ infoFIBS[FIBSHost], &ret, (QWidget *)parent());
+
+ if (ret)
+ infoFIBS[FIBSHost] = msg;
+ else
+ return false;
+
+ }
+ if (newlogin || infoFIBS[FIBSPort].isEmpty()) {
+
+ msg = KLineEditDlg::getText(i18n("Enter the port number on the server. "
+ "It should almost always be \"4321\"."),
+ infoFIBS[FIBSPort], &ret, (QWidget *)parent());
+
+ if (ret)
+ infoFIBS[FIBSPort] = msg;
+ else
+ return false;
+ }
+ if (newlogin || infoFIBS[FIBSUser].isEmpty()) {
+
+ if (newlogin)
+
+ text = i18n("Enter the login you would like to use on the server %1. The login may not\n"
+ "contain spaces or colons. If the login you choose is not available, you'll later be\n"
+ "given the opportunity to pick another one.\n\n").arg(infoFIBS[FIBSHost]);
+
+ else
+
+ text = i18n("Enter your login on the server %1. If you don't have a login, you\n"
+ "should create one using the corresponding menu option.\n\n").arg(infoFIBS[FIBSHost]);
+
+
+ first = true;
+ do {
+ msg = (KLineEditDlg::getText(text, infoFIBS[FIBSUser], &ret,
+ (QWidget *)parent())).stripWhiteSpace();
+ if (first) {
+ text += i18n("The login may not contain spaces or colons!");
+ first = false;
+ }
+
+ } while (ret && (msg.isEmpty() || msg.contains(' ') || msg.contains(':')));
+
+ if (ret)
+ infoFIBS[FIBSUser] = msg;
+ else
+ return false;
+ }
+ if (newlogin || infoFIBS[FIBSPswd].isEmpty()) {
+
+ if (newlogin)
+
+ text = i18n("Enter the password you would like to use with the login %1\n"
+ "on the server %2. It may not contain colons.\n\n").
+ arg(infoFIBS[FIBSUser]).arg(infoFIBS[FIBSHost]);
+
+ else
+
+ text = i18n("Enter the password for the login %1 on the server %2.\n\n").
+ arg(infoFIBS[FIBSUser]).arg(infoFIBS[FIBSHost]);
+
+ first = true;
+ do {
+ QCString password;
+ if (newlogin)
+ ret = (KPasswordDialog::getNewPassword(password, text) == KPasswordDialog::Accepted);
+ else
+ ret = (KPasswordDialog::getPassword(password, text) == KPasswordDialog::Accepted);
+
+ password.stripWhiteSpace();
+ msg = password;
+
+ if (first) {
+ text += i18n("The password may not contain colons or spaces!");
+ first = false;
+ }
+
+ } while (ret && (msg.isEmpty() || msg.contains(' ') || msg.contains(':')));
+
+ if (ret)
+ infoFIBS[FIBSPswd] = msg;
+ else
+ return false;
+ }
+
+ /*
+ * Made it here, all parameters acquired
+ */
+ return true;
+}
+
+
+// == message parsing ==========================================================
+
+/*
+ * Pattern setup - rather long and boring
+ */
+void KBgEngineFIBS::initPattern()
+{
+ QString pattern;
+
+ /*
+ * Initialize the search pattern array
+ */
+ pat[Welcome] = QRegExp(pattern.sprintf("^%d ", CLIP_WELCOME));
+ pat[OwnInfo] = QRegExp(pattern.sprintf("^%d ", CLIP_OWN_INFO));
+ pat[WhoInfo] = QRegExp(pattern.sprintf("^%d ", CLIP_WHO_INFO));
+ pat[WhoEnde] = QRegExp(pattern.sprintf("^%d$", CLIP_WHO_END));
+ pat[MotdBeg] = QRegExp(pattern.sprintf("^%d" , CLIP_MOTD_BEGIN));
+ pat[MotdEnd] = QRegExp(pattern.sprintf("^%d" , CLIP_MOTD_END));
+ pat[MsgPers] = QRegExp(pattern.sprintf("^%d ", CLIP_MESSAGE));
+ pat[MsgDeli] = QRegExp(pattern.sprintf("^%d ", CLIP_MESSAGE_DELIVERED));
+ pat[MsgSave] = QRegExp(pattern.sprintf("^%d ", CLIP_MESSAGE_SAVED));
+ pat[ChatSay] = QRegExp(pattern.sprintf("^%d ", CLIP_SAYS));
+ pat[ChatSht] = QRegExp(pattern.sprintf("^%d ", CLIP_SHOUTS));
+ pat[ChatWis] = QRegExp(pattern.sprintf("^%d ", CLIP_WHISPERS));
+ pat[ChatKib] = QRegExp(pattern.sprintf("^%d ", CLIP_KIBITZES));
+ pat[SelfSay] = QRegExp(pattern.sprintf("^%d ", CLIP_YOU_SAY));
+ pat[SelfSht] = QRegExp(pattern.sprintf("^%d ", CLIP_YOU_SHOUT));
+ pat[SelfWis] = QRegExp(pattern.sprintf("^%d ", CLIP_YOU_WHISPER));
+ pat[SelfKib] = QRegExp(pattern.sprintf("^%d ", CLIP_YOU_KIBITZ));
+ pat[UserLin] = QRegExp(pattern.sprintf("^%d ", CLIP_LOGIN));
+ pat[UserLot] = QRegExp(pattern.sprintf("^%d ", CLIP_LOGOUT));
+
+ pat[NoLogin] = QRegExp("\\*\\* Unknown command: 'login'");
+ pat[BegRate] = QRegExp("^rating calculation:$");
+ pat[EndRate] = QRegExp("^change for ");
+ pat[HTML_lt] = QRegExp("<");
+ pat[HTML_gt] = QRegExp(">");
+ pat[BoardSY] = QRegExp("^Value of 'boardstyle' set to 3");
+ pat[BoardSN] = QRegExp("^Value of 'boardstyle' set to [^3]");
+ pat[WhoisBG] = QRegExp("^Information about ");
+ pat[WhoisE1] = QRegExp("^ No email address\\.$");
+ pat[WhoisE2] = QRegExp("^ Email address: ");
+ pat[SelfSlf] = QRegExp("^You say to yourself:");
+ pat[Goodbye] = QRegExp("^ Goodbye\\.");
+ pat[GameSav] = QRegExp("The game was saved\\.$");
+ pat[RawBord] = QRegExp("^board:");
+ pat[YouTurn] = QRegExp("^It's your turn\\. Please roll or double");
+ pat[PlsMove] = QRegExp("^Please move [1-6]+ pie");
+ pat[EndWtch] = QRegExp("^You stop watching ");
+ pat[BegWtch] = QRegExp("^You're now watching ");
+ pat[BegGame] = QRegExp("^Starting a new game with ");
+ pat[Reload1] = QRegExp("^You are now playing with ");
+ pat[Reload2] = QRegExp(" has joined you. Your running match was loaded\\.$");
+ pat[OneWave] = QRegExp(" waves goodbye.$");
+ pat[TwoWave] = QRegExp(" waves goodbye again.$");
+ pat[YouWave] = QRegExp("^You wave goodbye.$");
+ pat[GameBG1] = QRegExp("start a [0-9]+ point match");
+ pat[GameBG2] = QRegExp("start an unlimited match");
+ pat[GameRE1] = QRegExp("are resuming their [0-9]+-point match");
+ pat[GameRE2] = QRegExp("are resuming their unlimited match");
+ pat[GameEnd] = QRegExp("point match against");
+ pat[TabChar] = QRegExp("\\t");
+ pat[PlsChar] = QRegExp("\\+");
+ pat[Invite0] = QRegExp(" wants to play a [0-9]+ point match with you\\.$");
+ pat[Invite1] = QRegExp("^.+ wants to play a ");
+ pat[Invite2] = QRegExp(" wants to resume a saved match with you\\.$");
+ pat[Invite3] = QRegExp(" wants to play an unlimited match with you\\.$");
+ pat[TypJoin] = QRegExp("^Type 'join ");
+ pat[OneName] = QRegExp("^ONE USERNAME PER PERSON ONLY!!!");
+ pat[YouAway] = QRegExp("^You're away. Please type 'back'");
+ pat[YouBack] = QRegExp("^Welcome back\\.$");
+ pat[YouMove] = QRegExp("^It's your turn to move\\.");
+ pat[YouRoll] = QRegExp("^It's your turn to roll or double\\.");
+ pat[TwoStar] = QRegExp("^\\*\\* ");
+ pat[OthrNam] = QRegExp("^\\*\\* Please use another name\\. ");
+ pat[BoxHori] = QRegExp("^ *\\+-*\\+ *$");
+ pat[BoxVer1] = QRegExp("^ *\\|");
+ pat[BoxVer2] = QRegExp("\\| *$");
+ pat[YourNam] = QRegExp("Your name will be ");
+ pat[GivePwd] = QRegExp("Please give your password:");
+ pat[RetypeP] = QRegExp("Please retype your password:");
+ pat[HelpTxt] = QRegExp("^NAME$");
+ pat[MatchB1] = QRegExp(" has joined you for a [0-9]+ point match\\.$");
+ pat[MatchB2] = QRegExp(" has joined you for an unlimited match\\.$");
+ pat[EndLose] = QRegExp(" wins the [0-9]+ point match [0-9]+-[0-9]+");
+ pat[EndVict] = QRegExp(" win the [0-9]+ point match [0-9]+-[0-9]+");
+ pat[RejAcpt] = QRegExp("Type 'accept' or 'reject'\\.$");
+ pat[YouAcpt] = QRegExp("^You accept the double\\. The cube shows [0-9]+\\.");
+
+ pat[KeepAlv] = QRegExp("^\\*\\* Unknown command: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'");
+ pat[RatingY] = QRegExp("You'll see how the rating changes are calculated\\.$");
+ pat[RatingN] = QRegExp("You won't see how the rating changes are calculated\\.$");
+
+ // FIXME same problem as in previous line
+ // mpgnu accepts the double.5 arthur_tn - gnu 1 0 1243.32 365 6 983722411 adsl-61-168-141.bna.bellsouth.net - -
+
+ // FIXME: <PLAYER> can't move. -- needs board reload...
+
+ /*
+
+ opponent matchlength score (your points first)
+ **gnu 1 0 - 0
+ *blah 1 0 - 0
+ kraut 1 0 - 0
+
+ logged in and ready **
+ logged in *
+ otherwise " "
+
+ */
+
+ pat[ConLeav] = QRegExp("^Type 'join' if you want to play the next game, type 'leave' if you don't\\.$");
+ pat[GreedyY] = QRegExp("^\\*\\* Will use automatic greedy bearoffs\\.");
+ pat[GreedyN] = QRegExp("^\\*\\* Won't use automatic greedy bearoffs\\.");
+ pat[BegBlnd] = QRegExp("^\\*\\* You blind ");
+ pat[EndBlnd] = QRegExp("^\\*\\* You unblind ");
+ pat[MatchB3] = QRegExp("^\\*\\* You are now playing a [0-9]+ point match with ");
+ pat[MatchB4] = QRegExp("^\\*\\* You are now playing an unlimited match with ");
+ pat[RejCont] = QRegExp("^You reject\\. The game continues\\.");
+ pat[AcptWin] = QRegExp("^You accept and win ");
+ pat[YouGive] = QRegExp("^You give up\\.");
+ pat[DoubleY] = QRegExp("^\\*\\* You will be asked if you want to double\\.");
+ pat[DoubleN] = QRegExp("^\\*\\* You won't be asked if you want to double\\.");
+}
+
+/*
+ * Parse an incoming line and notify all interested parties - first match
+ * decides.
+ */
+void KBgEngineFIBS::handleServerData(QString &line)
+{
+ QString rawline = line; // contains the line before it is HTML'fied
+
+ /*
+ * Fix-up any HTML-like tags in the line
+ */
+ line.replace(pat[HTML_lt], "&lt;");
+ line.replace(pat[HTML_gt], "&gt;");
+
+ /*
+ * FIBS sometimes sends tabs, where it should send 8 spaces...
+ */
+ line.replace(pat[TabChar], " ");
+
+ switch (rxStatus) {
+
+ case RxConnect:
+ handleMessageConnect(line, rawline);
+ break;
+
+ case RxMotd:
+ handleMessageMotd(line);
+ return;
+
+ case RxWhois:
+ handleMessageWhois(line);
+ break;
+
+ case RxRating:
+ handleMessageRating(line);
+ break;
+
+ case RxNewLogin:
+ handleMessageNewLogin(line);
+ break;
+
+ case RxIgnore:
+ /*
+ * Ignore _ALL_ incoming strings - this is needed during the
+ * login phase, when the message box is open.
+ */
+ break;
+
+ case RxGoodbye:
+ /*
+ * Receive the logout sequence. The string will be flushed by the
+ * disconnectFIBS() callback
+ */
+ rxCollect += QString("<font color=\"blue\"><pre>") + line + "</pre></font><br>";
+ break;
+
+ case RxNormal:
+ handleMessageNormal(line, rawline);
+ break;
+
+ default:
+ /*
+ * This is a serious problem - latin1() is fine since the line comes from FIBS.
+ */
+ std::cerr << "PROBLEM in KBgEngineFIBS::handleServerData: " << line.latin1() << std::endl;
+ }
+}
+
+/*
+ * Handle messages during the RxWhois state
+ */
+void KBgEngineFIBS::handleMessageWhois(const QString &line)
+{
+ rxCollect += "<br>&nbsp;&nbsp;&nbsp;&nbsp;" + line;
+ if (line.contains(pat[WhoisE1]) || line.contains(pat[WhoisE2])) {
+ rxStatus = RxNormal;
+ emit infoText("<font color=\"darkgreen\">" + rxCollect + "<br></font>");
+ }
+}
+
+/*
+ * Handle messages during the RxRating state
+ */
+void KBgEngineFIBS::handleMessageRating(const QString &line)
+{
+ rxCollect += "<br>" + line;
+ if (line.contains(pat[EndRate]) && ++rxCount == 2) {
+ emit infoText("<font color=\"blue\">" + rxCollect + "<br></font>");
+ rxStatus = RxNormal;
+ }
+}
+
+/*
+ * Handle messages during the RxMotd state
+ */
+void KBgEngineFIBS::handleMessageMotd(const QString &line)
+{
+ if (line.contains(pat[MotdEnd])) {
+ rxStatus = RxNormal;
+ emit infoText("<font color=\"blue\"><pre>" + rxCollect + "</pre></font>");
+ /*
+ * just to be on the safe side, we set the value of boardstyle.
+ * we do it here, since this is reasonably late in the login
+ * procedure
+ */
+ emit serverString("set boardstyle 3");
+ } else {
+ QString tline = line;
+ tline.replace(pat[BoxHori], "<br><hr>");
+ tline.replace(pat[BoxVer1], "");
+ tline.replace(pat[BoxVer2], "");
+ rxCollect += "<br>" + tline;
+ }
+}
+
+/*
+ * Handle messages during the RxConnect state
+ */
+void KBgEngineFIBS::handleMessageConnect(const QString &line, const QString &rawline)
+{
+ /*
+ * Two possibilities: either we are logged in or we sent bad password/login
+ */
+ if (line.contains("login:")) {
+ /*
+ * This can only happen if the password/login is wrong.
+ */
+ if (rxCollect.isEmpty()) {
+ rxStatus = RxIgnore;
+ int ret = KMessageBox::warningContinueCancel
+ ((QWidget *)parent(), i18n("There was a problem with "
+ "your login and password. "
+ "You can reenter\n"
+ "your login and password and "
+ "try to reconnect."),
+ i18n("Wrong Login/Password"),
+ i18n("Reconnect"));
+ if (ret == KMessageBox::Continue) {
+ infoFIBS[FIBSUser] = "";
+ infoFIBS[FIBSPswd] = "";
+ login = true;
+ connectFIBS(); // will reset the rxStatus
+ } else {
+ rxStatus = RxConnect;
+ emit serverString("");
+ emit serverString("");
+ }
+ return;
+ }
+ emit infoText("<hr><pre>" + rxCollect + "</pre><br>");
+ rxCollect = "";
+ return;
+ }
+
+ /*
+ * Ok, we are logged in! Now receive personal information. These
+ * are completely useless but what the heck.
+ */
+ if (line.contains(pat[Welcome])) {
+ char p[3][256];
+ time_t tmp;
+ // Using latin1() is okay, since the string comes from FIBS.
+ int words = sscanf (line.latin1(), "%255s%255s%li%255s", p[0], p[1], &tmp, p[2]);
+ if (words >= 4) {
+ QDateTime d; d.setTime_t(tmp);
+ QString text = i18n("%1, last logged in from %2 at %3.").arg(p[1]).arg(p[2]).arg(d.toString());
+ emit infoText("<hr><br>" + text);
+ playerlist->setName(p[1]);
+ }
+ return;
+ }
+
+ /*
+ * Initial parsing of user options and making sure that settings needed
+ * by us are at the correct value. We use and ignore values according
+ * to the following list:
+ *
+ * p[ 0] - CLIP_OWN_INFO
+ * p[ 1] - name -- IGNORE
+ * OptAllowPip
+ * n[ 0] - autoboard -- IGNORE
+ * OptAutoDouble
+ * OptAutoMove
+ * n[ 1] - away -- IGNORE
+ * n[ 2] - bell -- IGNORE
+ * OptCrawford
+ * n[ 3] - double -- IGNORE
+ * n[ 4] - expierience -- IGNORE
+ * OptGreedy
+ * n[ 6] - moreboards -- IGNORE and set to YES
+ * OptMoves
+ * n[ 8] - notify -- IGNORE and set to YES
+ * rating - rating -- IGNORE
+ * OptRatings
+ * OptReady
+ * n[10] - redoubles -- IGNORE
+ * n[11] - report -- IGNORE and set to YES
+ * OptSilent
+ * p[3] - timezone
+ *
+ */
+ if (line.contains(pat[OwnInfo])) {
+
+ rxStatus = RxNormal;
+
+ int fibsOptions[NumFIBSOpt];
+
+ char p[3][256];
+ int n[12];
+ double rating;
+
+ // Using latin1() is okay, since the string comes from FIBS.
+ int words = sscanf (line.latin1(), "%255s%255s%i%i%i%i%i%i%i%i%i%i%i%i%i%lf%i%i%i%i%i%255s",
+ p[0], p[1],
+ &fibsOptions[OptAllowPip],
+ &n[0],
+ &fibsOptions[OptDouble],
+ &fibsOptions[OptAutoMove], // equivalent to OptDouble, can be ignored
+ &n[1], &n[2],
+ &fibsOptions[OptCrawford],
+ &n[3], &n[4],
+ &fibsOptions[OptGreedy],
+ &n[6],
+ &fibsOptions[OptMoves],
+ &n[8],
+ &rating,
+ &fibsOptions[OptRatings],
+ &fibsOptions[OptReady],
+ &n[10], &n[11],
+ &fibsOptions[OptSilent],
+ p[2]);
+
+ if (words >= 22 && n[6] != 1) {
+ /*
+ * need to get boards after new dice have arrived
+ */
+ emit infoText("<font color=\"red\">" + i18n("The moreboards toggle has been set.") + "</font>");
+ emit serverString("toggle moreboards");
+ }
+ if (words >= 22 && n[8] != 1) {
+ /*
+ * need to know who logs out
+ */
+ emit infoText("<font color=\"red\">" + i18n("The notify toggle has been set.") + "</font>");
+ emit serverString("toggle notify");
+ }
+ if (words >= 22 && n[11] != 1) {
+ /*
+ * want to know who starts playing games
+ */
+ emit infoText("<font color=\"red\">" + i18n("The report toggle has been set.") + "</font>");
+ emit serverString("toggle report");
+ }
+
+ /*
+ * Set the correct toggles in the options menu
+ */
+ fibsOpt[OptReady]->setChecked(fibsOptions[OptReady]);
+ fibsOpt[OptDouble]->setChecked(!fibsOptions[OptDouble]);
+ fibsOpt[OptRatings]->setChecked(fibsOptions[OptRatings]);
+
+ return;
+ }
+
+ /*
+ * The beginning of a new login procedure starts starts here
+ */
+ if (line.contains(pat[OneName])) {
+ rxStatus = RxNewLogin;
+ emit infoText(QString("<font color=\"red\">") + rxCollect + "</font>");
+ rxCollect = "";
+ QString tmp = rawline;
+ handleServerData(tmp);
+ return;
+ }
+
+ /*
+ * Still in the middle of the login sequence, still collecting information
+ */
+ rxCollect += "<br>" + line;
+}
+
+/*
+ * Handle messages during the RxNewLogin state
+ */
+void KBgEngineFIBS::handleMessageNewLogin(const QString &line)
+{
+ /*
+ * Request the new login
+ */
+ if (line.contains(pat[OneName])) {
+ emit serverString(QString("name ") + infoFIBS[FIBSUser]);
+ return;
+ }
+ /*
+ * Ooops, user name already exists
+ */
+ if (line.contains(pat[OthrNam])) {
+ QString text = i18n("The selected login is alreay in use! Please select another one.");
+ bool ret, first = true;
+ QString msg;
+
+ do {
+ msg = (KLineEditDlg::getText(text, infoFIBS[FIBSUser], &ret,
+ (QWidget *)parent())).stripWhiteSpace();
+ if (first) {
+ text += i18n("\n\nThe login may not contain spaces or colons!");
+ first = false;
+ }
+ } while (msg.contains(' ') || msg.contains(':'));
+
+ if (ret) {
+ infoFIBS[FIBSUser] = msg;
+ emit serverString("name " + msg);
+ } else
+ emit serverString("bye");
+
+ return;
+ }
+ /*
+ * first time we send the password
+ */
+ if (line.contains(pat[YourNam])) {
+ emit serverString(infoFIBS[FIBSPswd]);
+ return;
+ }
+ /*
+ * second time we send the password
+ */
+ if (line.contains(pat[GivePwd])) {
+ emit serverString(infoFIBS[FIBSPswd]);
+ return;
+ }
+ /*
+ * at this point we are done creating the account
+ */
+ if (line.contains(pat[RetypeP])) {
+
+ QString text = i18n("Your account has been created. Your new login is <u>%1</u>. To fully activate "
+ "this account, I will now close the connection. Once you reconnect, you can start "
+ "playing backgammon on FIBS.").arg(infoFIBS[FIBSUser]);
+ emit infoText("<br><hr><font color=\"blue\">" + text + "</font><br><hr>");
+ emit serverString("bye");
+ rxStatus = RxNormal;
+ rxCollect = "";
+ return;
+ }
+ return;
+}
+
+/*
+ * Handle all normal messages - during the RxNormal state
+ */
+void KBgEngineFIBS::handleMessageNormal(QString &line, QString &rawline)
+{
+
+ // - ignored ----------------------------------------------------------------------
+
+ /*
+ * For now, the waves are ignored. They should probably go into
+ * the chat window -- but only optional
+ */
+ if (line.contains(pat[OneWave]) || line.contains(pat[TwoWave]) || line.contains(pat[YouWave])) {
+
+ return;
+ }
+
+ /*
+ * These messages used to go into the games window. If KBackgammon
+ * ever gets a games window, they should be in there. For now, they
+ * are ignored.
+ */
+ else if (line.contains(pat[GameBG1]) || line.contains(pat[GameBG2]) || line.contains(pat[GameRE1]) ||
+ line.contains(pat[GameRE2]) || line.contains(pat[GameEnd])) {
+
+ return;
+ }
+
+ /*
+ * Artefact caused by the login test procedure utilized.
+ */
+ else if (line.contains(pat[NoLogin])) {
+
+ return;
+ }
+
+ /*
+ * Connection keep-alive response
+ */
+ else if (line.contains(pat[KeepAlv])) {
+
+ return;
+ }
+
+ // --------------------------------------------------------------------------------
+
+ /*
+ * Chat and personal messages - note that the chat window sends these messages
+ * back to us so we can display them if the user wants that.
+ */
+ else if (line.contains(pat[ChatSay]) || line.contains(pat[ChatSht]) || line.contains(pat[ChatWis]) ||
+ line.contains(pat[ChatKib]) || line.contains(pat[SelfSay]) || line.contains(pat[SelfSht]) ||
+ line.contains(pat[SelfWis]) || line.contains(pat[SelfKib]) || line.contains(pat[SelfSlf]) ||
+ line.contains(pat[MsgPers]) || line.contains(pat[MsgDeli]) || line.contains(pat[MsgSave])) {
+
+ emit chatMessage(line);
+ return;
+ }
+
+ // --------------------------------------------------------------------------------
+
+ /*
+ * Beginning of games. In all these cases we are playing and not watching.
+ */
+ else if (line.contains(pat[MatchB1]) || line.contains(pat[MatchB2])) {
+
+ if (useAutoMsg[MsgBeg] && !autoMsg[MsgBeg].stripWhiteSpace().isEmpty())
+ emit serverString("kibitz " + autoMsg[MsgBeg]);
+ }
+ else if (line.contains(pat[MatchB3]) || line.contains(pat[MatchB4])) {
+
+ if (useAutoMsg[MsgBeg] && !autoMsg[MsgBeg].stripWhiteSpace().isEmpty())
+ emit serverString("kibitz " + autoMsg[MsgBeg]);
+ line = "<font color=\"red\">" + line + "</font>";
+ }
+
+ // --------------------------------------------------------------------------------
+
+ /*
+ * The help should be handled separately. A fairly complete implementation of a
+ * help parsing can be found in KFibs.
+ */
+ else if (line.contains(pat[HelpTxt])) {
+
+ // do nothing
+ }
+
+ // --------------------------------------------------------------------------------
+
+ /*
+ * Simple cases without the need for many comments...
+ */
+ else if (line.contains(pat[RawBord])) {
+
+ /*
+ * Save the board string and create a new game state
+ */
+ KBgStatus *st = new KBgStatus(currBoard = rawline);
+
+ /*
+ * Save important state data and stop the timeout
+ */
+ ct->stop();
+ undoCounter = 0;
+
+ pname[US ] = st->player(US);
+ pname[THEM] = st->player(THEM);
+
+ playing = (QString("You") == pname[US]);
+
+ toMove = st->moves();
+
+ /*
+ * Update the caption string
+ */
+ if (st->turn() < 0)
+ caption = i18n("%1 (%2) vs. %3 (%4) - game over").arg(pname[US]).
+ arg(st->points(US)).arg(pname[THEM]).arg(st->points(THEM));
+ else if (st->length() < 0)
+ caption = i18n("%1 (%2) vs. %3 (%4) - unlimited match").arg(pname[US]).
+ arg(st->points(US)).arg(pname[THEM]).arg(st->points(THEM));
+ else
+ caption = i18n("%1 (%2) vs. %3 (%4) - %5 point match").arg(pname[US]).
+ arg(st->points(US)).arg(pname[THEM]).arg(st->points(THEM)).
+ arg(st->length());
+
+ emit statText(caption);
+
+ /*
+ * Emit information and drop the state object
+ */
+ emit allowMoving(playing && (st->turn() == US));
+ emit newState(*st);
+
+ delete st;
+
+ /*
+ * Set the actions correctly
+ */
+ emit allowCommand(Load, true );
+ emit allowCommand(Undo, false);
+ emit allowCommand(Redo, false);
+ emit allowCommand(Done, false);
+
+ return;
+ }
+ else if (line.contains(pat[PlsMove]) || line.contains(pat[YouMove])) {
+
+ KNotifyClient::event("move", i18n("Please make your move"));
+
+ }
+
+ // --------------------------------------------------------------------------------
+
+ /*
+ * Being away and coming back
+ */
+ else if (line.contains(pat[YouAway])) {
+
+ emit changePlayerStatus(infoFIBS[FIBSUser], KFibsPlayerList::Away, true);
+ actBack->setEnabled(true);
+ line += "<br><pre> </pre>" + i18n("(or use the corresponding menu entry to join the match)");
+ }
+ else if (line.contains(pat[YouBack])) {
+
+ emit changePlayerStatus(infoFIBS[FIBSUser], KFibsPlayerList::Away, false);
+ actBack->setEnabled(false);
+ actAway->setEnabled(true);
+ }
+
+ // --------------------------------------------------------------------------------
+
+ /*
+ * Catch the response of the user responding to double or resign
+ */
+ else if (line.contains(pat[YouGive]) || line.contains(pat[RejCont]) || line.contains(pat[AcptWin])) {
+
+ actAccept->setEnabled(false);
+ actReject->setEnabled(false);
+ }
+
+ // --------------------------------------------------------------------------------
+
+ /*
+ * Catch the responses to newly set toggles
+ */
+ else if (line.contains(pat[GreedyY]) || line.contains(pat[GreedyN])) {
+
+ fibsOpt[OptGreedy]->setChecked(line.contains(pat[GreedyY]));
+ line = "<font color=\"red\">" + line + "</font>";
+ }
+ else if (line.contains(pat[DoubleY]) || line.contains(pat[DoubleN])) {
+
+ fibsOpt[OptDouble]->setChecked(line.contains(pat[DoubleY]));
+ line = "<font color=\"red\">" + line + "</font>";
+ }
+
+ else if (line.contains(pat[RatingY]) || line.contains(pat[RatingN])) {
+
+ fibsOpt[OptRatings]->setChecked(line.contains(pat[RatingY]));
+ line = "<font color=\"red\">" + line + "</font>";
+ }
+
+ // --------------------------------------------------------------------------------
+
+ /*
+ * It's our turn to roll or double
+ */
+ else if (line.contains(pat[YouTurn]) || line.contains(pat[YouRoll])) {
+
+ emit allowCommand(Cube, playing);
+ emit allowCommand(Roll, playing);
+
+ emit statText(caption); // force a pip count recomputation by the board
+
+ KNotifyClient::event("roll or double", i18n("It's your turn to roll the dice or double the cube"));
+ }
+
+ // --------------------------------------------------------------------------------
+
+ /*
+ * Got an invitation for a match
+ */
+ else if (line.contains(pat[Invite0]) || line.contains(pat[Invite2]) || line.contains(pat[Invite3])) {
+
+ rxCollect = rawline.left(rawline.find(' '));
+ emit serverString("rawwho " + rxCollect);
+
+ if (line.contains(pat[Invite0])) {
+ rawline.replace(pat[Invite1], "");
+ rawline = rxCollect + " "+ rawline.left(rawline.find(' '));
+ } else if (line.contains(pat[Invite2])) {
+ rawline = rxCollect + " r";
+ } else if (line.contains(pat[Invite3])) {
+ invitations += rxCollect + " u";
+ }
+ invitations += rawline;
+ return; // will be printed once the rawwho is received
+ }
+
+ // - rx status changes ------------------------------------------------------------
+
+ else if (line.contains(pat[WhoisBG])) {
+ rxStatus = RxWhois;
+ rxCollect = QString("<br><u>") + line + "</u>";
+ return;
+ }
+ else if (line.contains(pat[MotdBeg])) {
+ rxStatus = RxMotd;
+ rxCollect = "";
+ return;
+ }
+ else if (line.contains(pat[BegRate])) {
+ rxStatus = RxRating;
+ rxCount = 0;
+ rxCollect = "<br>" + line;
+ return;
+ }
+ else if (line.contains(pat[Goodbye])) {
+ rxStatus = RxGoodbye;
+ rxCollect = "<br><hr><br>";
+ handleServerData(rawline); // danger: recursion!
+ return;
+ }
+
+ // --------------------------------------------------------------------------------
+ // --------------------------------------------------------------------------------
+ // --------------------------------------------------------------------------------
+ // --------------------------------------------------------------------------------
+
+ /*
+ * Continue a mutli game match? We have to either leave or continue
+ */
+ else if (line.contains(pat[ConLeav])) {
+ actConti->setEnabled(true);
+ actLeave->setEnabled(true);
+ line.append("<br><pre> </pre>" + i18n("(or use the corresponding menu "
+ "entry to leave or continue the match)"));
+ }
+ /*
+ * Beginning and end of user updates
+ */
+ else if (line.contains(pat[WhoInfo])) {
+ rawline.replace(pat[WhoInfo], "");
+ if (rawline.contains(QRegExp("^" + infoFIBS[FIBSUser] + " "))) {
+ int ready;
+ // Using latin1() is fine, since the string is coming from FIBS.
+ sscanf(rawline.latin1(), "%*s %*s %*s %i %*s %*s %*s %*s %*s %*s %*s %*s", &ready);
+ fibsOpt[OptReady]->setChecked(ready);
+ }
+ emit fibsWhoInfo(rawline);
+ return;
+ }
+ else if (line.contains(pat[WhoEnde])) {
+ emit fibsWhoEnd();
+ return;
+ }
+ /*
+ * This message is ignored. The instruction is given elsewhere (and slightly
+ * delayed in the flow of time).
+ */
+ if (line.contains(pat[TypJoin])) {
+ return;
+ }
+ /*
+ * Watching other players
+ */
+ else if (line.contains(pat[BegWtch])) {
+ emit allowCommand(Load, true);
+ rawline.replace(pat[BegWtch], "");
+ rawline.truncate(rawline.length()-1);
+ emit fibsStartNewGame(rawline);
+ load();
+ }
+ else if (line.contains(pat[EndWtch])) {
+ emit gameOver();
+ }
+ /*
+ * Blinding of players, the actual blind is handled by
+ * the player list
+ */
+ else if (line.contains(pat[BegBlnd])) {
+ rawline.replace(pat[BegBlnd], "");
+ rawline.truncate(rawline.length()-1);
+ emit changePlayerStatus(rawline, KFibsPlayerList::Blind, true);
+ line = "<font color=\"red\">" + line + "</font>";
+ }
+ else if (line.contains(pat[EndBlnd])) {
+ rawline.replace(pat[EndBlnd], "");
+ rawline.truncate(rawline.length()-1);
+ emit changePlayerStatus(rawline, KFibsPlayerList::Blind, false);
+ line = "<font color=\"red\">" + line + "</font>";
+ }
+ /*
+ * Starting or reloading games or matches
+ */
+ else if (line.contains(pat[BegGame])) {
+ rawline.replace(pat[BegGame], "");
+ rawline.truncate(rawline.length()-1);
+ emit fibsStartNewGame(rawline);
+ fibsOpt[OptDouble]->setChecked(true);
+ fibsOpt[OptGreedy]->setChecked(false);
+ actConti->setEnabled(false);
+ actLeave->setEnabled(false);
+ }
+ else if (line.contains(pat[Reload1])) {
+ rawline.replace(pat[Reload1], "");
+ rawline = rawline.left(rawline.find(' '));
+ rawline.truncate(rawline.length()-1);
+ emit fibsStartNewGame(rawline);
+ fibsOpt[OptDouble]->setChecked(true);
+ fibsOpt[OptGreedy]->setChecked(false);
+ actConti->setEnabled(false);
+ actLeave->setEnabled(false);
+ load();
+ }
+ else if (line.contains(pat[Reload2])) {
+ rawline.replace(pat[Reload2], "");
+ emit fibsStartNewGame(rawline);
+ fibsOpt[OptDouble]->setChecked(true);
+ fibsOpt[OptGreedy]->setChecked(false);
+ actConti->setEnabled(false);
+ actLeave->setEnabled(false);
+ load();
+ }
+ /*
+ * Opponent offered resignation or the cube. We have to accept
+ * or reject the offer.
+ */
+ else if (line.contains(pat[RejAcpt])) {
+ actAccept->setEnabled(true);
+ actReject->setEnabled(true);
+ line += "<br><pre> </pre>" + i18n("(or use the corresponding menu "
+ "entry to accept or reject the offer)");
+ }
+ /*
+ * This is strange: FIBS seems to not send a newline at the
+ * end of this pattern. So we work around that.
+ */
+ else if (line.contains(pat[YouAcpt])) {
+ actAccept->setEnabled(false);
+ actReject->setEnabled(false);
+ rawline.replace(pat[YouAcpt], "");
+ line.truncate(line.length()-rawline.length());
+ if (!rawline.stripWhiteSpace().isEmpty()) {
+ handleServerData(rawline);
+ }
+ }
+ /*
+ * Ending of games
+ */
+ else if (line.contains(pat[EndLose])) {
+ if (playing) {
+ KNotifyClient::event("game over l", i18n("Sorry, you lost the game."));
+ if (useAutoMsg[MsgLos] && !autoMsg[MsgLos].stripWhiteSpace().isEmpty())
+ emit serverString(QString("tell ") + pname[THEM] + " " + autoMsg[MsgLos]);
+ }
+ emit gameOver();
+ }
+ else if (line.contains(pat[EndVict])) {
+ if (playing) {
+ KNotifyClient::event("game over w", i18n("Congratulations, you won the game!"));
+ if (useAutoMsg[MsgWin] && !autoMsg[MsgWin].stripWhiteSpace().isEmpty())
+ emit serverString(QString("tell ") + pname[THEM] + " " + autoMsg[MsgWin]);
+ }
+ emit gameOver();
+ }
+ else if (line.contains(pat[GameSav])) {
+ emit gameOver();
+ }
+ /*
+ * User logs out. This has to be signalled to the player
+ * list. Get the true user names by working on the rawline.
+ */
+ else if (line.contains(pat[UserLot])) {
+ rawline.replace(pat[UserLot], "");
+ emit fibsLogout(rawline.left(rawline.find(' ')));
+ return;
+ }
+ /*
+ * Emit the name of the newly logged in user.
+ */
+ else if (line.contains(pat[UserLin])) {
+ rawline.replace(pat[UserLin], "");
+ emit fibsLogin(rawline.left(rawline.find(' ')));
+ return;
+ }
+ /*
+ * Special attention has to be paid to the proper setting of
+ * the 'boardstyle' variable, since we will not be able to display
+ * the board properly without it.
+ */
+ else if (line.contains(pat[BoardSY])) {
+ // ignored
+ return;
+ }
+ else if (line.contains(pat[BoardSN])) {
+ emit serverString("set boardstyle 3");
+ emit infoText(QString("<font color=\"red\"><br>")
+ + i18n("You should never set the 'boardstyle' variable "
+ "by hand! It is vital for proper functioning of "
+ "this program that it remains set to 3. It has "
+ "been reset for you.")
+ + "<br></font>");
+ return;
+ }
+ /*
+ * This is the final fall through: if the line started with ** and
+ * hasn't been processed, make it red, since it is a server resp.
+ * to something we just did.
+ */
+ else if (line.contains(pat[TwoStar])) {
+ line = "<font color=\"red\">" + line + "</font>";
+ }
+
+ // --------------------------------------------------------------------------------
+
+ /*
+ * Print whatever part of the line made it here
+ */
+ emit infoText(line);
+}
+
+// EOF
+
+
+// == constructor, destructor and setup ========================================
+
+/*
+ * Constructor
+ */
+KBgEngineFIBS::KBgEngineFIBS(QWidget *parent, QString *name, QPopupMenu *pmenu)
+ : KBgEngine(parent, name, pmenu)
+{
+ /*
+ * No connection, not playing, ready for login
+ */
+ connection = new QSocket(parent, "fibs connection");
+ playing = false;
+ login = true;
+
+ connect(connection, SIGNAL(hostFound()), this, SLOT(hostFound()));
+ connect(connection, SIGNAL(connected()), this, SLOT(connected()));
+ connect(connection, SIGNAL(error(int)), this, SLOT(connError(int)));
+ connect(connection, SIGNAL(connectionClosed()), this, SLOT(connectionClosed()));
+ connect(connection, SIGNAL(delayedCloseFinished()), this, SLOT(connectionClosed()));
+ connect(connection, SIGNAL(readyRead()), this, SLOT(readData()));
+
+ connect(this, SIGNAL(serverString(const QString &)), this, SLOT(sendData(const QString &)));
+
+ /*
+ * No invitation dialog
+ */
+ invitationDlg = 0;
+
+ connect(this, SIGNAL(fibsWhoInfo(const QString &)), this, SLOT(changeJoin(const QString &)));
+ connect(this, SIGNAL(fibsLogout (const QString &)), this, SLOT(cancelJoin(const QString &)));
+ connect(this, SIGNAL(gameOver()), this, SLOT(endGame()));
+
+ /*
+ * Creating, initializing and connecting the player list
+ */
+ playerlist = new KFibsPlayerList(0, "fibs player list");
+
+ connect(this, SIGNAL(fibsWhoInfo(const QString &)), playerlist, SLOT(changePlayer(const QString &)));
+ connect(this, SIGNAL(fibsLogout (const QString &)), playerlist, SLOT(deletePlayer(const QString &)));
+ connect(this, SIGNAL(fibsWhoEnd()), playerlist, SLOT(stopUpdate()));
+ connect(this, SIGNAL(fibsConnectionClosed()), playerlist, SLOT(stopUpdate()));
+ connect(this, SIGNAL(changePlayerStatus(const QString &, int, bool)),
+ playerlist, SLOT(changePlayerStatus(const QString &, int, bool)));
+ connect(playerlist, SIGNAL(fibsCommand(const QString &)), this, SLOT(handleCommand(const QString &)));
+ connect(playerlist, SIGNAL(fibsInvite(const QString &)), this, SLOT(fibsRequestInvitation(const QString &)));
+
+ /*
+ * Create, initialize and connect the chat window
+ */
+ chatWindow = new KBgChat(0, "chat window");
+
+ connect(this, SIGNAL(chatMessage(const QString &)), chatWindow, SLOT(handleData(const QString &)));
+ connect(this, SIGNAL(fibsStartNewGame(const QString &)), chatWindow, SLOT(startGame(const QString &)));
+ connect(this, SIGNAL(gameOver()), chatWindow, SLOT(endGame()));
+ connect(this, SIGNAL(fibsLogout (const QString &)), chatWindow, SLOT(deletePlayer(const QString &)));
+ connect(chatWindow, SIGNAL(fibsCommand(const QString &)), this, SLOT(handleCommand(const QString &)));
+ connect(chatWindow, SIGNAL(fibsRequestInvitation(const QString &)), this, SLOT(fibsRequestInvitation(const QString &)));
+ connect(chatWindow, SIGNAL(personalMessage(const QString &)), this, SLOT(personalMessage(const QString &)));
+ connect(playerlist, SIGNAL(fibsTalk(const QString &)), chatWindow, SLOT(fibsTalk(const QString &)));
+
+ /*
+ * Creating, initializing and connecting the menu
+ * ----------------------------------------------
+ */
+ respMenu = new QPopupMenu();
+ joinMenu = new QPopupMenu();
+ cmdMenu = new QPopupMenu();
+ optsMenu = new QPopupMenu();
+
+ /*
+ * Initialize the FIBS submenu - this is also put in the play menu
+ */
+ conAction = new KAction(i18n("&Connect"), 0, this, SLOT( connectFIBS()), this);
+ newAction = new KAction(i18n("New Account"), 0, this, SLOT( newAccount()), this);
+ disAction = new KAction(i18n("&Disconnect"), 0, this, SLOT(disconnectFIBS()), this);
+
+ conAction->setEnabled(true ); conAction->plug(menu);
+ disAction->setEnabled(false); disAction->plug(menu);
+ newAction->setEnabled(true ); newAction->plug(menu);
+
+ menu->insertSeparator();
+
+ (invAction = new KAction(i18n("&Invite..."), 0, this, SLOT(inviteDialog()), this))->plug(menu);
+
+ /*
+ * Create and fill the response menu. This is for all these: type this or
+ * that messages from FIBS.
+ */
+ cmdMenuID = menu->insertItem(i18n("&Commands"), cmdMenu); {
+
+ (actAway = new KAction(i18n("Away"), 0, this, SLOT(away()), this))->plug(cmdMenu);
+ (actBack = new KAction(i18n("Back"), 0, this, SLOT(back()), this))->plug(cmdMenu);
+
+ actAway->setEnabled(true);
+ actBack->setEnabled(false);
+ }
+
+ /*
+ * Create the server side options. This is preliminary and needs more work.
+ * The available options are skewed, since they refelect the needs of the
+ * author. Contact jens@hoefkens.com if your favorite option is missing.
+ */
+ optsMenuID = menu->insertItem(i18n("&Options"), optsMenu); {
+
+ for (int i = 0; i < NumFIBSOpt; i++)
+ fibsOpt[i] = 0;
+
+ fibsOpt[OptReady] = new KToggleAction(i18n("Ready to Play"),
+ 0, this, SLOT(toggle_ready()), this);
+ fibsOpt[OptRatings] = new KToggleAction(i18n("Show Rating Computations"),
+ 0, this, SLOT(toggle_ratings()), this);
+ fibsOpt[OptRatings]->setCheckedState(i18n("Hide Rating Computations"));
+ fibsOpt[OptGreedy] = new KToggleAction(i18n("Greedy Bearoffs"),
+ 0, this, SLOT(toggle_greedy()), this);
+ fibsOpt[OptDouble] = new KToggleAction(i18n("Ask for Doubles"),
+ 0, this, SLOT(toggle_double()), this);
+
+ for (int i = 0; i < NumFIBSOpt; i++)
+ if (fibsOpt[i])
+ fibsOpt[i]->plug(optsMenu);
+
+ }
+
+ /*
+ * Create and fill the response menu. This is for all these: type this or
+ * that messages from FIBS.
+ */
+ respMenuID = menu->insertItem(i18n("&Response"), respMenu); {
+
+ (actAccept = new KAction(i18n("Accept"), 0, this, SLOT(accept()), this))->plug(respMenu);
+ (actReject = new KAction(i18n("Reject"), 0, this, SLOT(reject()), this))->plug(respMenu);
+
+ actAccept->setEnabled(false);
+ actReject->setEnabled(false);
+
+ respMenu->insertSeparator();
+
+ (actConti = new KAction(i18n("Join"), 0, this, SLOT(match_conti()), this))->plug(respMenu);
+ (actLeave = new KAction(i18n("Leave"), 0, this, SLOT(match_leave()), this))->plug(respMenu);
+
+ actConti->setEnabled(false);
+ actLeave->setEnabled(false);
+ }
+
+ /*
+ * Create the join menu and do not fill it (this happens at first
+ * action setup.
+ */
+ joinMenuID = menu->insertItem(i18n("&Join"), joinMenu); {
+ numJoin = -1;
+
+ actJoin[0] = new KAction("", 0, this, SLOT(join_0()), this);
+ actJoin[1] = new KAction("", 0, this, SLOT(join_1()), this);
+ actJoin[2] = new KAction("", 0, this, SLOT(join_2()), this);
+ actJoin[3] = new KAction("", 0, this, SLOT(join_3()), this);
+ actJoin[4] = new KAction("", 0, this, SLOT(join_4()), this);
+ actJoin[5] = new KAction("", 0, this, SLOT(join_5()), this);
+ actJoin[6] = new KAction("", 0, this, SLOT(join_6()), this);
+ actJoin[7] = new KAction("", 0, this, SLOT(join_7()), this);
+ }
+
+ menu->setItemEnabled(joinMenuID, false);
+ menu->setItemEnabled( cmdMenuID, false);
+ menu->setItemEnabled(respMenuID, false);
+ menu->setItemEnabled(optsMenuID, false);
+
+ /*
+ * Continue with the FIBS menu
+ */
+ menu->insertSeparator();
+
+ (listAct = new KToggleAction(i18n("&Player List"), 0, this, SLOT(showList()), this))->plug(menu);
+ (chatAct = new KToggleAction(i18n("&Chat"), 0, this, SLOT(showChat()), this))->plug(menu);
+
+ connect(playerlist, SIGNAL(windowVisible(bool)), listAct, SLOT(setChecked(bool)));
+ connect(chatWindow, SIGNAL(windowVisible(bool)), chatAct, SLOT(setChecked(bool)));
+
+ /*
+ * Create message IDs. This sets up a lot of regular expressions.
+ */
+ initPattern();
+
+ /*
+ * Restore old settings
+ */
+ readConfig();
+
+ // FIXME: open the child windows in start() and not here
+
+ /*
+ * Update the menu actions
+ */
+ listAct->setChecked(playerlist->isVisible());
+ chatAct->setChecked(chatWindow->isVisible());
+
+ /*
+ * Initialize the keepalive timer FIXME: make this a setting
+ */
+ keepalive = true;
+
+ // FIXME: move the start to connect...
+
+ keepaliveTimer = new QTimer(this);
+ connect(keepaliveTimer, SIGNAL(timeout()), this, SLOT(keepAlive()));
+ keepaliveTimer->start(1200000);
+}
+
+/*
+ * Destructor deletes child objects if necessary
+ */
+KBgEngineFIBS::~KBgEngineFIBS()
+{
+ delete joinMenu;
+ delete respMenu;
+ delete cmdMenu;
+ delete optsMenu;
+
+ delete connection;
+ delete invitationDlg;
+
+ delete playerlist;
+ delete chatWindow;
+}
+
+
diff --git a/kbackgammon/engines/fibs/kbgfibs.h b/kbackgammon/engines/fibs/kbgfibs.h
new file mode 100644
index 00000000..1c14e0f3
--- /dev/null
+++ b/kbackgammon/engines/fibs/kbgfibs.h
@@ -0,0 +1,479 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+
+#ifndef __KBGFIBS_H
+#define __KBGFIBS_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <generic/kbgengine.h>
+
+#include "kplayerlist.h"
+#include "kbgfibschat.h"
+#include "kbginvite.h" // TODO
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qregexp.h>
+
+#include <klocale.h>
+
+class QTimer;
+class QSocket;
+class QPopupMenu;
+class QCheckBox;
+
+class KAction;
+class KToggleAction;
+
+/**
+ *
+ * Special backgammon engine for games on the First Internet Backgammon Server
+ *
+ * @short The FIBS backgammon engine
+ * @author Jens Hoefkens <jens@hoefkens.com>
+ *
+ */
+class KBgEngineFIBS : public KBgEngine
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * Constructor
+ */
+ KBgEngineFIBS(QWidget *parent = 0, QString *name = 0, QPopupMenu *pmenu = 0);
+
+ /**
+ * Destructor
+ */
+ virtual ~KBgEngineFIBS();
+
+ /**
+ * Fills the engine-specific page into the notebook
+ */
+ virtual void getSetupPages(KDialogBase *nb);
+
+ virtual void setupOk();
+ virtual void setupDefault();
+ virtual void setupCancel();
+
+ /*
+ * Check with the engine if we can quit. This may require user
+ * interaction.
+ */
+ virtual bool queryClose();
+
+ /**
+ * About to be closed. Let the engine exit properly.
+ */
+ virtual bool queryExit();
+
+ virtual void start();
+
+
+public slots:
+
+ /**
+ * Read and save user settings to the config file
+ */
+ virtual void readConfig();
+ virtual void saveConfig();
+
+ /**
+ * Roll dice for the player w
+ */
+ virtual void rollDice(const int w);
+
+ /**
+ * Double the cube of player w
+ */
+ virtual void doubleCube(const int w);
+
+ /**
+ * A move has been made on the board - see the board class
+ * for the format of the string s
+ */
+ virtual void handleMove(QString *s);
+
+ /**
+ * Undo the last move
+ */
+ virtual void undo();
+
+ /**
+ * Redo the last move
+ */
+ virtual void redo();
+
+ /**
+ * Commit a move
+ */
+ virtual void done();
+
+
+ // ###########################################################################
+ //
+ //
+ //
+ // TODO TODO TODO TODO TODO TODO TODO
+ //
+ //
+ //
+ // ###########################################################################
+
+ /*
+ * Process the string cmd
+ */
+ void handleCommand(const QString &cmd);
+
+ void fibsRequestInvitation(const QString &player);
+
+ void personalMessage(const QString &msg);
+
+
+
+ /*
+ * Local configuration handling
+ */
+
+ void keepAlive();
+
+signals:
+
+ void serverString(const QString &s);
+
+ void fibsWhoInfo(const QString &line);
+ void fibsWhoEnd();
+ void fibsLogout(const QString &p);
+ void fibsLogin(const QString &p);
+
+ void fibsConnectionClosed();
+
+ void changePlayerStatus(const QString &, int, bool);
+
+ void chatMessage(const QString &msg);
+
+ void fibsStartNewGame(const QString &msg);
+ void gameOver();
+
+protected slots:
+
+ void invitationDone();
+ void inviteDialog();
+ void showList();
+ void showChat();
+
+ void endGame();
+
+private:
+
+ QTimer *keepaliveTimer;
+
+ QString pname[2];
+
+ QString currBoard, caption;
+
+ //KBgStatus *currBoard
+ //KBgFIBSBoard *boardHandler;
+
+ QStringList invitations;
+
+ /*
+ * special menu entries
+ */
+ int respMenuID, cmdMenuID, joinMenuID, optsMenuID;
+ QPopupMenu *respMenu, *cmdMenu, *joinMenu, *optsMenu;
+
+ /*
+ * child windows
+ */
+ KFibsPlayerList *playerlist;
+ KBgChat *chatWindow;
+ KBgInvite *invitationDlg;
+
+ /*
+ * Other stuff
+ */
+ QString lastMove;
+ int toMove;
+
+ QString lastAway;
+ bool playing;
+ bool redoPossible;
+ int undoCounter;
+
+ KAction *conAction, *disAction, *newAction, *invAction;
+
+ KAction *actAccept, *actReject, *actConti, *actLeave, *actAway, *actBack;
+
+ KToggleAction *chatAct, *listAct;
+
+
+ // ###########################################################################
+ //
+ //
+ //
+ // DONE DONE DONE DONE DONE DONE DONE
+ //
+ //
+ //
+ // ###########################################################################
+
+private:
+
+ /**
+ * Actions for responding to invitations. numJoin is he current
+ * number of active actions. The max. number of pending invitations
+ * is eight and this is hardcoded in a lot of places (not the least
+ * of which are the slots join_N().
+ */
+ KAction *actJoin[8];
+ int numJoin;
+
+protected slots:
+
+ /**
+ * Handle rawwho information for the purposes of the invitation
+ * submenu and the join entries
+ */
+ void changeJoin(const QString &info);
+
+ /**
+ * A player will be removed from the menu of pending invitations
+ * if necessary.
+ */
+ void cancelJoin(const QString &info);
+
+ /**
+ * We have up to 8 names in the join menu. They are the
+ * players that invited us to play games. Each action
+ * has its own slot and all slots call the common backend
+ * join().
+ */
+ void join(const QString &msg);
+
+ void join_0();
+ void join_1();
+ void join_2();
+ void join_3();
+ void join_4();
+ void join_5();
+ void join_6();
+ void join_7();
+
+ /**
+ * Simple slots that toggle FIBS server-side settings. The
+ * names of the functions reflect the name of the toggle on
+ * FIBS.
+ */
+ void toggle_greedy();
+ void toggle_ready();
+ void toggle_double();
+ void toggle_ratings();
+
+private:
+
+ /**
+ * Toggle actions for the FIBS server-side settings
+ */
+ enum FIBSOpt {OptReady, OptGreedy, OptDouble,
+ OptAllowPip, OptAutoMove, OptCrawford, OptSilent, OptRatings, OptMoves, NumFIBSOpt};
+ KToggleAction *fibsOpt[NumFIBSOpt];
+
+public slots:
+
+ /*
+ * Connection handling
+ * -------------------
+ */
+
+ // initiate asynchronous connection establishment
+ void connectFIBS();
+
+ // take the connection down
+ void disconnectFIBS();
+
+ // create a new account and connect
+ void newAccount();
+
+ // called when the connection is down
+ void connectionClosed();
+
+ // the hostname has been resolved
+ void hostFound();
+
+ // a connection error occurred
+ void connError(int f);
+
+ // connection has been established
+ void connected();
+
+ // data can be read from the socket
+ void readData();
+
+ // send the string s to the server
+ void sendData(const QString &s);
+
+protected:
+
+ // get the connection parameters
+ bool queryConnection(const bool newlogin);
+
+private:
+
+ // actual connection object
+ QSocket *connection;
+
+ // flag if we have login information or new account
+ bool login;
+
+protected slots:
+
+ /*
+ * FIBS command slots
+ * ------------------
+ */
+
+ // go away and leave a message
+ void away();
+
+ // come back after being away
+ void back();
+
+ // roll dice
+ virtual void roll();
+
+ // double the cube
+ virtual void cube();
+
+ // reload the board to the last known sane state
+ virtual void load();
+
+ // accept an offer
+ void accept();
+
+ // reject an offer
+ void reject();
+
+ // continue a multi game match
+ void match_conti();
+
+ // leave a multi game match
+ void match_leave();
+
+protected slots:
+
+ /*
+ * All strings received from the server are given to handleServerData() for
+ * identification and processing. It implements a limited state machine to
+ * handle the incoming data correctly. The whole function could probably be
+ * made more efficient, but it is not time critical (and it appears to be
+ * easier to understand this way).
+ */
+ void handleServerData(QString &line);
+
+protected:
+
+ enum RxStatus {RxIgnore, RxConnect, RxWhois, RxMotd, RxRating,
+ RxNewLogin, RxGoodbye, RxNormal};
+
+ int rxStatus, rxCount;
+
+ QString rxCollect;
+
+ /*
+ * The following functions handle the individual states
+ * of the handleServerData() state machine,
+ */
+ void handleMessageWhois(const QString &line);
+ void handleMessageRating(const QString &line);
+ void handleMessageMotd(const QString &line);
+ void handleMessageNewLogin(const QString &line);
+ void handleMessageConnect(const QString &line, const QString &rawline);
+ void handleMessageNormal(QString &line, QString &rawline);
+
+ /*
+ * The next enumeration and the array of regular expressions is needed for the
+ * message identification in handleServerData().
+ */
+ enum Pattern {Welcome, OwnInfo, NoLogin, BegRate, EndRate, HTML_lt, HTML_gt,
+ BoardSY, BoardSN, WhoisBG, WhoisE1, WhoisE2, WhoEnde, WhoInfo,
+ MotdBeg, MotdEnd, MsgPers, MsgDeli, MsgSave, ChatSay, ChatSht,
+ ChatWis, ChatKib, SelfSay, SelfSlf, SelfSht, SelfWis, SelfKib,
+ UserLin, UserLot, Goodbye, GameSav, RawBord, YouTurn, PlsMove,
+ BegWtch, EndWtch, BegBlnd, EndBlnd, BegGame, OneWave, TwoWave,
+ YouWave, Reload1, Reload2, GameBG1, GameBG2, GameRE1, GameRE2,
+ GameEnd, EndLose, EndVict, MatchB1, MatchB2, MatchB3, MatchB4,
+ RejAcpt, YouAway, YouAcpt, HelpTxt, Invite0, Invite1, Invite2,
+ Invite3, ConLeav, TabChar, PlsChar, OneName, TypJoin, YouBack,
+ YouMove, YouRoll, TwoStar, BoxHori, BoxVer1, BoxVer2, OthrNam,
+ YourNam, GivePwd, RetypeP, GreedyY, GreedyN, RejCont, AcptWin,
+ YouGive, DoubleY, DoubleN, KeepAlv, RatingY, RatingN,
+ NumPattern};
+
+ QRegExp pat[NumPattern];
+
+ /*
+ * This function is simply filling the pat[] array with the proper values.
+ */
+ void initPattern();
+
+private:
+
+ /*
+ * Local setup and config variables
+ * ================================
+ */
+
+ /*
+ * Various options
+ */
+ bool showMsg, whoisInvite;
+ QCheckBox *cbp, *cbi;
+
+ QCheckBox *cbk;
+ bool keepalive;
+
+ /*
+ * Connection setup
+ */
+ enum FIBSInfo {FIBSHost, FIBSPort, FIBSUser, FIBSPswd, NumFIBS};
+ QString infoFIBS[NumFIBS];
+ QLineEdit *lec[NumFIBS];
+
+ /*
+ * Auto messages
+ */
+ enum AutoMessages {MsgBeg, MsgLos, MsgWin, NumMsg};
+ QLineEdit *lem[NumMsg];
+ QCheckBox *cbm[NumMsg];
+ bool useAutoMsg[NumMsg];
+ QString autoMsg[NumMsg];
+};
+
+#endif // __KBGFIBS_H
diff --git a/kbackgammon/engines/fibs/kbgfibschat.cpp b/kbackgammon/engines/fibs/kbgfibschat.cpp
new file mode 100644
index 00000000..45ba2bb7
--- /dev/null
+++ b/kbackgammon/engines/fibs/kbgfibschat.cpp
@@ -0,0 +1,828 @@
+
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+
+#include "kbgfibschat.h"
+#include "kbgfibschat.moc"
+
+#include <qstring.h>
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qpopupmenu.h>
+#include <qregexp.h>
+#include <qfont.h>
+#include <qwhatsthis.h>
+#include <qdatetime.h>
+#include <qclipboard.h>
+#include <qsimplerichtext.h>
+#include <qregion.h>
+#include <qpalette.h>
+#include <qpainter.h>
+#include <qpoint.h>
+#include <qlistbox.h>
+#include <qiconset.h>
+#include <qstringlist.h>
+#include <qdict.h>
+
+#include <klocale.h>
+#include <kconfig.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kstdaction.h>
+#include <ktabctl.h>
+#include <kaction.h>
+#include <kiconloader.h>
+
+#include "clip.h"
+#include "version.h"
+
+
+/*
+ * Private utility class that might become more generally useful in
+ * the future. Basically, it implements rich text QListBox items.
+ */
+class KLBT : public QListBoxText
+{
+
+public:
+
+ /*
+ * Constructor
+ */
+ KLBT(QWidget *parent, const QString &text = QString::null, const QString &player = QString::null)
+ : QListBoxText(text)
+ {
+ w = parent;
+ n = new QString(player);
+ t = new QSimpleRichText(text, w->font());
+
+ // FIXME: this is not yet perfect
+ t->setWidth(w->width()-20);
+ }
+
+ /*
+ * Destructor
+ */
+ virtual ~KLBT()
+ {
+ delete t;
+ delete n;
+ }
+
+ /*
+ * Overloaded required members returning height
+ */
+ virtual int height(const QListBox *) const
+ {
+ return (1+t->height());
+ }
+
+ /*
+ * Overloaded required members returning width
+ */
+ virtual int width(const QListBox *) const
+ {
+ return t->width();
+ }
+
+ /*
+ * The context menu needs the name of the player. It's easier
+ * than extracting it from the text.
+ */
+ QString player() const
+ {
+ return *n;
+ }
+
+protected:
+
+ /*
+ * Required overloaded member to paint the text on the painter p.
+ */
+ virtual void paint(QPainter *p)
+ {
+ t->draw(p, 1, 1, QRegion(p->viewport()), w->colorGroup());
+ }
+
+private:
+
+ QSimpleRichText *t;
+ QWidget *w;
+ QString *n;
+
+};
+
+
+class KBgChatPrivate
+{
+public:
+
+ /*
+ * Name of the users
+ */
+ QString mName[2];
+
+ /*
+ * Hold and assemble info text
+ */
+ QString mText;
+
+ /*
+ * Numbers of the private action list.
+ */
+ enum Privact {Inquire, InviteD, Invite1, Invite2, Invite3, Invite4,
+ Invite5, Invite6, Invite7, InviteR, InviteU, Silent,
+ Talk, Gag, Ungag, Cleargag, Copy, Clear, Close, MaxAction};
+
+ /*
+ * Available actions
+ */
+ KAction *mAct[MaxAction];
+
+ /*
+ * Context menu and invitation menu
+ */
+ QPopupMenu *mChat, *mInvt;
+
+ /*
+ * list of users we do not want to hear shouting
+ */
+ QStringList mGag;
+
+ /*
+ * Listbox needed by the setup dialog
+ */
+ QListBox *mLb;
+
+ /*
+ * Internal ID to name mapping
+ */
+ QDict<int> *mName2ID;
+
+};
+
+
+// == constructor, destructor ==================================================
+
+/*
+ * Constructor of the chat window.
+ */
+KBgChat::KBgChat(QWidget *parent, const char *name)
+ : KChat(parent, false)
+{
+ d = new KBgChatPrivate();
+ KActionCollection* actions = new KActionCollection(this);
+
+ d->mName[0] = QString::null;
+ d->mChat = 0;
+ d->mInvt = new QPopupMenu();
+
+ setAutoAddMessages(false); // we get an echo from FIBS
+ setFromNickname(i18n("%1 user").arg(PROG_NAME));
+
+ if (!addSendingEntry(i18n("Kibitz to watchers and players"), CLIP_YOU_KIBITZ))
+ kdDebug(10500) << "adding kibitz" << endl;
+ if (!addSendingEntry(i18n("Whisper to watchers only"), CLIP_YOU_WHISPER))
+ kdDebug(10500) << "adding whisper" << endl;
+
+ connect(this, SIGNAL(rightButtonClicked(QListBoxItem *, const QPoint &)),
+ this, SLOT(contextMenu(QListBoxItem *, const QPoint &)));
+ connect(this, SIGNAL(signalSendMessage(int, const QString &)),
+ this, SLOT(handleCommand(int, const QString &)));
+
+ d->mName2ID = new QDict<int>(17, true);
+ d->mName2ID->setAutoDelete(true);
+
+ /*
+ * some eye candy :)
+ */
+ setIcon(kapp->miniIcon());
+ setCaption(i18n("Chat Window"));
+
+ QWhatsThis::add(this, i18n("This is the chat window.\n\n"
+ "The text in this window is colored depending on whether "
+ "it is directed at you personally, shouted to the general "
+ "FIBS population, has been said by you, or is of general "
+ "interest. If you select the name of a player, the context "
+ "contains entries specifically geared towards that player."));
+ /*
+ * Define set of available actions
+ */
+ d->mAct[KBgChatPrivate::Inquire] = new KAction(i18n("Info On"),
+ QIconSet(kapp->iconLoader()->loadIcon(
+ "help.xpm", KIcon::Small)),
+ 0, this, SLOT(slotInquire()), actions);
+ d->mAct[KBgChatPrivate::Talk] = new KAction(i18n("Talk To"),
+ QIconSet(kapp->iconLoader()->loadIcon(
+ PROG_NAME "-chat.png", KIcon::Small)),
+ 0, this, SLOT(slotTalk()), actions);
+
+ d->mAct[KBgChatPrivate::InviteD] = new KAction(i18n("Use Dialog"), 0, this,
+ SLOT(slotInviteD()), actions);
+ d->mAct[KBgChatPrivate::Invite1] = new KAction(i18n("1 Point Match"), 0, this,
+ SLOT(slotInvite1()), actions);
+ d->mAct[KBgChatPrivate::Invite2] = new KAction(i18n("2 Point Match"), 0, this,
+ SLOT(slotInvite2()), actions);
+ d->mAct[KBgChatPrivate::Invite3] = new KAction(i18n("3 Point Match"), 0, this,
+ SLOT(slotInvite3()), actions);
+ d->mAct[KBgChatPrivate::Invite4] = new KAction(i18n("4 Point Match"), 0, this,
+ SLOT(slotInvite4()), actions);
+ d->mAct[KBgChatPrivate::Invite5] = new KAction(i18n("5 Point Match"), 0, this,
+ SLOT(slotInvite5()), actions);
+ d->mAct[KBgChatPrivate::Invite6] = new KAction(i18n("6 Point Match"), 0, this,
+ SLOT(slotInvite6()), actions);
+ d->mAct[KBgChatPrivate::Invite7] = new KAction(i18n("7 Point Match"), 0, this,
+ SLOT(slotInvite7()), actions);
+ d->mAct[KBgChatPrivate::InviteU] = new KAction(i18n("Unlimited"), 0, this,
+ SLOT(slotInviteU()), actions);
+ d->mAct[KBgChatPrivate::InviteR] = new KAction(i18n("Resume"), 0, this,
+ SLOT(slotInviteR()), actions);
+
+ d->mAct[KBgChatPrivate::InviteD]->plug(d->mInvt);
+
+ d->mInvt->insertSeparator();
+
+ d->mAct[KBgChatPrivate::Invite1]->plug(d->mInvt);
+ d->mAct[KBgChatPrivate::Invite2]->plug(d->mInvt);
+ d->mAct[KBgChatPrivate::Invite3]->plug(d->mInvt);
+ d->mAct[KBgChatPrivate::Invite4]->plug(d->mInvt);
+ d->mAct[KBgChatPrivate::Invite5]->plug(d->mInvt);
+ d->mAct[KBgChatPrivate::Invite6]->plug(d->mInvt);
+ d->mAct[KBgChatPrivate::Invite7]->plug(d->mInvt);
+
+ d->mInvt->insertSeparator();
+
+ d->mAct[KBgChatPrivate::InviteU]->plug(d->mInvt);
+ d->mAct[KBgChatPrivate::InviteR]->plug(d->mInvt);
+
+ d->mAct[KBgChatPrivate::Gag] = new KAction(i18n("Gag"), 0, this, SLOT(slotGag()), actions);
+ d->mAct[KBgChatPrivate::Ungag] = new KAction(i18n("Ungag"), 0, this, SLOT(slotUngag()), actions);
+ d->mAct[KBgChatPrivate::Cleargag] = new KAction(i18n("Clear Gag List"), 0, this, SLOT(slotCleargag()), actions);
+ d->mAct[KBgChatPrivate::Copy] = KStdAction::copy(this, SLOT(slotCopy()), actions);
+ d->mAct[KBgChatPrivate::Clear] = new KAction(i18n("Clear"), 0, this, SLOT(slotClear()), actions);
+ d->mAct[KBgChatPrivate::Close] = KStdAction::close(this, SLOT(hide()), actions);
+ d->mAct[KBgChatPrivate::Silent] = new KToggleAction(i18n("Silent"), 0, this, SLOT(slotSilent()), actions);
+}
+
+
+/*
+ * Destructor
+ */
+KBgChat::~KBgChat()
+{
+ delete d->mName2ID;
+ delete d->mChat; // save to delete NULL pointers
+ delete d->mInvt;
+ delete d;
+}
+
+
+// == configuration handling ===================================================
+
+/*
+ * Restore the previously stored settings
+ */
+void KBgChat::readConfig()
+{
+ KConfig* config = kapp->config();
+ config->setGroup("chat window");
+
+ QPoint pos(10, 10);
+
+ pos = config->readPointEntry("ori", &pos);
+ setGeometry(pos.x(), pos.y(), config->readNumEntry("wdt",460), config->readNumEntry("hgt",200));
+
+ config->readBoolEntry("vis", false) ? show() : hide();
+
+ ((KToggleAction *)d->mAct[KBgChatPrivate::Silent])->setChecked(config->readBoolEntry("sil", false));
+
+ d->mGag = config->readListEntry("gag");
+}
+
+/*
+ * Save the current settings to disk
+ */
+void KBgChat::saveConfig()
+{
+ KConfig* config = kapp->config();
+ config->setGroup("chat window");
+
+ config->writeEntry("ori", pos());
+ config->writeEntry("hgt", height());
+ config->writeEntry("wdt", width());
+ config->writeEntry("vis", isVisible());
+
+ config->writeEntry("sil", ((KToggleAction *)d->mAct[KBgChatPrivate::Silent])->isChecked());
+
+ config->writeEntry("gag", d->mGag);
+}
+
+
+/*
+ * Setup dialog page of the player list - allow the user to select the
+ * columns to show
+ *
+ * FIXME: need to be able to set font here KChatBase::setBothFont(const QFont& font)
+ */
+void KBgChat::getSetupPages(KTabCtl *nb, int space)
+{
+ /*
+ * Main Widget
+ * ===========
+ */
+ QWidget *w = new QWidget(nb);
+ QGridLayout *gl = new QGridLayout(w, 2, 1, space);
+
+ d->mLb = new QListBox(w);
+ d->mLb->setMultiSelection(true);
+
+ d->mLb->insertStringList(d->mGag);
+
+ QLabel *info = new QLabel(w);
+ info->setText(i18n("Select users to be removed from the gag list."));
+
+ QWhatsThis::add(w, i18n("Select all the users you want "
+ "to remove from the gag list "
+ "and then click OK. Afterwards "
+ "you will again hear what they shout."));
+
+ gl->addWidget(d->mLb, 0, 0);
+ gl->addWidget(info, 1, 0);
+
+ /*
+ * put in the page
+ * ===============
+ */
+ gl->activate();
+ w->adjustSize();
+ w->setMinimumSize(w->size());
+ nb->addTab(w, i18n("&Gag List"));
+}
+
+/*
+ * Remove all the selected entries from the gag list
+ */
+void KBgChat::setupOk()
+{
+ for (uint i = 0; i < d->mLb->count(); ++i) {
+ if (d->mLb->isSelected(i))
+ d->mGag.remove(d->mLb->text(i));
+ }
+ d->mLb->clear();
+ d->mLb->insertStringList(d->mGag);
+}
+
+/*
+ * Don't do anything
+ */
+void KBgChat::setupCancel()
+{
+ // empty
+}
+
+/*
+ * By default, all players stay in the gag list
+ */
+void KBgChat::setupDefault()
+{
+ d->mLb->clearSelection();
+}
+
+
+// == various slots and functions ==============================================
+
+/*
+ * Overloaded member to create a QListBoxItem for the chat window.
+ */
+QListBoxItem* KBgChat::layoutMessage(const QString& fromName, const QString& text)
+{
+ QListBoxText* message = new KLBT(this, text, fromName);
+ return message;
+}
+
+/*
+ * Catch hide events, so the engine's menu can be update.
+ */
+void KBgChat::showEvent(QShowEvent *e)
+{
+ QFrame::showEvent(e);
+ emit windowVisible(true);
+}
+
+/*
+ * Catch hide events, so the engine's menu can be update.
+ */
+void KBgChat::hideEvent(QHideEvent *e)
+{
+ emit windowVisible(false);
+ QFrame::hideEvent(e);
+}
+
+/*
+ * At the beginning of a game, add the name to the list and switch to
+ * kibitz mode.
+ */
+void KBgChat::startGame(const QString &name)
+{
+ int *id = d->mName2ID->find(d->mName[1] = name);
+ if (!id) {
+ id = new int(nextId());
+ d->mName2ID->insert(name, id);
+ addSendingEntry(i18n("Talk to %1").arg(name), *id);
+ }
+ setSendingEntry(CLIP_YOU_KIBITZ);
+}
+
+/*
+ * At the end of a game, we switch to talk mode.
+ */
+void KBgChat::endGame()
+{
+ int *id = d->mName2ID->find(d->mName[1]);
+ if (id)
+ setSendingEntry(*id);
+ else
+ setSendingEntry(SendToAll);
+}
+
+/*
+ * Set the chat window ready to talk to name
+ */
+void KBgChat::fibsTalk(const QString &name)
+{
+ int *id = d->mName2ID->find(name);
+ if (!id) {
+ id = new int(nextId());
+ d->mName2ID->insert(name, id);
+ addSendingEntry(i18n("Talk to %1").arg(name), *id);
+ }
+ setSendingEntry(*id);
+}
+
+/*
+ * Remove the player from the combo box when he/she logs out.
+ */
+void KBgChat::deletePlayer(const QString &name)
+{
+ int *id = d->mName2ID->find(name);
+ if (id) {
+ removeSendingEntry(*id);
+ d->mName2ID->remove(name);
+ }
+}
+
+/*
+ * Take action when the user presses return in the line edit control.
+ */
+void KBgChat::handleCommand(int id, const QString& msg)
+{
+ int realID = sendingEntry();
+
+ switch (realID) {
+ case SendToAll:
+ emit fibsCommand("shout " + msg);
+ break;
+ case CLIP_YOU_KIBITZ:
+ emit fibsCommand("kibitz " + msg);
+ break;
+ case CLIP_YOU_WHISPER:
+ emit fibsCommand("whisper " + msg);
+ break;
+ default:
+ QDictIterator<int> it(*d->mName2ID);
+ while (it.current()) {
+ if (*it.current() == realID) {
+ emit fibsCommand("tell " + it.currentKey() + " " + msg);
+ return;
+ }
+ ++it;
+ }
+ kdDebug(10500) << "unrecognized ID in KBgChat::handleCommand" << endl;
+ }
+}
+
+
+// == handle strings from the server ===========================================
+
+/*
+ * A message from the server that should be handled by us. If necessary,
+ * we replace the CLIP number by a string and put the line into the window.
+ *
+ * This function emits the string in rich text format with the signal
+ * personalMessage - again: the string contains rich text!
+ */
+void KBgChat::handleData(const QString &msg)
+{
+ QString clip = msg.left(msg.find(' ')), user, cMsg = msg;
+ QDateTime date;
+
+ bool flag = false;
+ int cmd = clip.toInt(&flag);
+
+ if (flag) {
+ cMsg.replace(0, cMsg.find(' ')+1, "");
+
+ user = cMsg.left(cMsg.find(' '));
+
+ switch (cmd) {
+ case CLIP_SAYS:
+ if (!d->mGag.contains(user)) {
+ cMsg = i18n("<u>%1 tells you:</u> %2").arg(user).arg(cMsg.replace(QRegExp("^" + user), ""));
+ cMsg = "<font color=\"red\">" + cMsg + "</font>";
+ emit personalMessage(cMsg);
+ } else
+ cMsg = "";
+ break;
+
+ case CLIP_SHOUTS:
+ if ((!((KToggleAction *)d->mAct[KBgChatPrivate::Silent])->isChecked()) && (!d->mGag.contains(user))) {
+ cMsg = i18n("<u>%1 shouts:</u> %2").arg(user).arg(cMsg.replace(QRegExp("^" + user), ""));
+ cMsg = "<font color=\"black\">" + cMsg + "</font>";
+ } else
+ cMsg = "";
+ break;
+
+ case CLIP_WHISPERS:
+ if (!d->mGag.contains(user)) {
+ cMsg = i18n("<u>%1 whispers:</u> %2").arg(user).arg(cMsg.replace(QRegExp("^" + user), ""));
+ cMsg = "<font color=\"red\">" + cMsg + "</font>";
+ emit personalMessage(cMsg);
+ } else
+ cMsg = "";
+ break;
+
+ case CLIP_KIBITZES:
+ if (!d->mGag.contains(user)) {
+ cMsg = i18n("<u>%1 kibitzes:</u> %2").arg(user).arg(cMsg.replace(QRegExp("^" + user), ""));
+ cMsg = "<font color=\"red\">" + cMsg + "</font>";
+ emit personalMessage(cMsg);
+ } else
+ cMsg = "";
+ break;
+
+ case CLIP_YOU_SAY:
+ cMsg = i18n("<u>You tell %1:</u> %2").arg(user).arg(cMsg.replace(QRegExp("^" + user), ""));
+ cMsg = "<font color=\"darkgreen\">" + cMsg + "</font>";
+ emit personalMessage(cMsg);
+ user = QString::null;
+ break;
+
+ case CLIP_YOU_SHOUT:
+ cMsg = i18n("<u>You shout:</u> %1").arg(cMsg);
+ cMsg = "<font color=\"darkgreen\">" + cMsg + "</font>";
+ emit personalMessage(cMsg);
+ user = QString::null;
+ break;
+
+ case CLIP_YOU_WHISPER:
+ cMsg = i18n("<u>You whisper:</u> %1").arg(cMsg);
+ cMsg = "<font color=\"darkgreen\">" + cMsg + "</font>";
+ emit personalMessage(cMsg);
+ user = QString::null;
+ break;
+
+ case CLIP_YOU_KIBITZ:
+ cMsg = i18n("<u>You kibitz:</u> %1").arg(cMsg);
+ cMsg = "<font color=\"darkgreen\">" + cMsg + "</font>";
+ emit personalMessage(cMsg);
+ user = QString::null;
+ break;
+
+ case CLIP_MESSAGE:
+ user = cMsg.left(cMsg.find(' ')+1);
+ cMsg.remove(0, cMsg.find(' ')+1);
+ date.setTime_t(cMsg.left(cMsg.find(' ')+1).toUInt());
+ cMsg.remove(0, cMsg.find(' '));
+ cMsg = i18n("<u>User %1 left a message at %2</u>: %3").arg(user).arg(date.toString()).arg(cMsg);
+ cMsg = "<font color=\"red\">" + cMsg + "</font>";
+ emit personalMessage(cMsg);
+ user = QString::null;
+ break;
+
+ case CLIP_MESSAGE_DELIVERED:
+ cMsg = i18n("Your message for %1 has been delivered.").arg(user);
+ cMsg = QString("<font color=\"darkgreen\">") + cMsg + "</font>";
+ emit personalMessage(cMsg);
+ user = QString::null;
+ break;
+
+ case CLIP_MESSAGE_SAVED:
+ cMsg = i18n("Your message for %1 has been saved.").arg(user);
+ cMsg = QString("<font color=\"darkgreen\">") + cMsg + "</font>";
+ emit personalMessage(cMsg);
+ user = QString::null;
+ break;
+
+ default: // ignore the message
+ return;
+ }
+
+ } else {
+
+ /*
+ * Special treatment for non-CLIP messages
+ */
+ if (cMsg.contains(QRegExp("^You say to yourself: "))) {
+ cMsg.replace(QRegExp("^You say to yourself: "),
+ i18n("<u>You say to yourself:</u> "));
+ } else {
+ kdDebug(user.isNull(), 10500) << "KBgChat::handleData unhandled message: "
+ << cMsg.latin1() << endl;
+ return;
+ }
+ }
+
+ if (!cMsg.isEmpty())
+ addMessage(user, cMsg);
+}
+
+
+// == context menu and related slots ===========================================
+
+/*
+ * RMB opens a context menu.
+ */
+void KBgChat::contextMenu(QListBoxItem *i, const QPoint &p)
+{
+ /*
+ * Even if i is non-null, user might still be QString::null
+ */
+ d->mName[0] = (i == 0) ? QString::null : ((KLBT *)i)->player();
+ d->mText = (i == 0) ? QString::null : ((KLBT *)i)->text();
+
+ /*
+ * Get a new context menu every time. Safe to delete the 0
+ * pointer.
+ */
+ delete d->mChat; d->mChat = new QPopupMenu();
+
+ /*
+ * Fill the context menu with actions
+ */
+ if (!d->mName[0].isNull()) {
+
+ d->mAct[KBgChatPrivate::Talk]->setText(i18n("Talk to %1").arg(d->mName[0]));
+ d->mAct[KBgChatPrivate::Talk]->plug(d->mChat);
+
+ d->mAct[KBgChatPrivate::Inquire]->setText(i18n("Info on %1").arg(d->mName[0]));
+ d->mAct[KBgChatPrivate::Inquire]->plug(d->mChat);
+
+ // invite menu is always the same
+ d->mChat->insertItem(i18n("Invite %1").arg(d->mName[0]), d->mInvt);
+
+ d->mChat->insertSeparator();
+
+ if (d->mGag.contains(d->mName[0]) <= 0) {
+ d->mAct[KBgChatPrivate::Gag]->setText(i18n("Gag %1").arg(d->mName[0]));
+ d->mAct[KBgChatPrivate::Gag]->plug(d->mChat);
+ } else {
+ d->mAct[KBgChatPrivate::Ungag]->setText(i18n("Ungag %1").arg(d->mName[0]));
+ d->mAct[KBgChatPrivate::Ungag]->plug(d->mChat);
+ }
+ }
+ if (d->mGag.count() > 0)
+ d->mAct[KBgChatPrivate::Cleargag]->plug(d->mChat);
+
+ if ((d->mGag.count() > 0) || (!d->mName[0].isNull()))
+ d->mChat->insertSeparator();
+
+ d->mAct[KBgChatPrivate::Silent]->plug(d->mChat);
+
+ d->mChat->insertSeparator();
+
+ d->mAct[KBgChatPrivate::Copy ]->plug(d->mChat);
+ d->mAct[KBgChatPrivate::Clear]->plug(d->mChat);
+ d->mAct[KBgChatPrivate::Close]->plug(d->mChat);
+
+ d->mChat->popup(p);
+}
+
+/*
+ * Clear the gag list
+ */
+void KBgChat::slotCleargag()
+{
+ d->mGag.clear();
+
+ QString msg("<font color=\"blue\">");
+ msg += i18n("The gag list is now empty.");
+ msg += "</font>";
+
+ addMessage(QString::null, msg);
+}
+
+/*
+ * Gag the selected user
+ */
+void KBgChat::slotGag()
+{
+ d->mGag.append(d->mName[0]);
+
+ QString msg("<font color=\"blue\">");
+ msg += i18n("You won't hear what %1 says and shouts.").arg(d->mName[0]);
+ msg += "</font>";
+
+ addMessage(QString::null, msg);
+}
+
+/*
+ * Simple interface to the actual talk slot
+ */
+void KBgChat::slotTalk()
+{
+ fibsTalk(d->mName[0]);
+}
+
+/*
+ * Remove selected user from gag list
+ */
+void KBgChat::slotUngag()
+{
+ d->mGag.remove(d->mName[0]);
+
+ QString msg("<font color=\"blue\">");
+ msg += i18n("You will again hear what %1 says and shouts.").arg(d->mName[0]);
+ msg += "</font>";
+
+ addMessage(QString::null, msg);
+}
+
+/*
+ * Get information on selected user
+ */
+void KBgChat::slotInquire()
+{
+ kdDebug(d->mName[0].isNull(), 10500) << "KBgChat::slotInquire: user == null" << endl;
+ emit fibsCommand("whois " + d->mName[0]);
+}
+
+/*
+ * Block all shouts from the chat window
+ */
+void KBgChat::slotSilent()
+{
+ QString msg;
+ if (((KToggleAction *)d->mAct[KBgChatPrivate::Silent])->isChecked())
+ msg = "<font color=\"blue\">" + i18n("You will not hear what people shout.") + "</font>";
+ else
+ msg = "<font color=\"blue\">" + i18n("You will hear what people shout.") + "</font>";
+ addMessage(QString::null, msg);
+}
+
+/*
+ * Copy the selected line to the clipboard. Strip the additional HTML
+ * from the text before copying.
+ */
+void KBgChat::slotCopy()
+{
+ d->mText.replace(QRegExp("<u>"), "");
+ d->mText.replace(QRegExp("</u>"), "");
+ d->mText.replace(QRegExp("</font>"), "");
+ d->mText.replace(QRegExp("^.*\">"), "");
+
+ kapp->clipboard()->setText(d->mText);
+}
+
+/*
+ * Invite the selected player.
+ */
+void KBgChat::slotInviteD()
+{
+ kdDebug(d->mName[0].isNull(), 10500) << "KBgChat::slotInvite: user == null" << endl;
+ emit fibsRequestInvitation(d->mName[0]);
+}
+void KBgChat::slotInvite1() { emit fibsCommand("invite " + d->mName[0] + " 1"); }
+void KBgChat::slotInvite2() { emit fibsCommand("invite " + d->mName[0] + " 2"); }
+void KBgChat::slotInvite3() { emit fibsCommand("invite " + d->mName[0] + " 3"); }
+void KBgChat::slotInvite4() { emit fibsCommand("invite " + d->mName[0] + " 4"); }
+void KBgChat::slotInvite5() { emit fibsCommand("invite " + d->mName[0] + " 5"); }
+void KBgChat::slotInvite6() { emit fibsCommand("invite " + d->mName[0] + " 6"); }
+void KBgChat::slotInvite7() { emit fibsCommand("invite " + d->mName[0] + " 7"); }
+
+void KBgChat::slotInviteU() { emit fibsCommand("invite " + d->mName[0] + " unlimited"); }
+void KBgChat::slotInviteR() { emit fibsCommand("invite " + d->mName[0]); }
+
+
+// EOF
diff --git a/kbackgammon/engines/fibs/kbgfibschat.h b/kbackgammon/engines/fibs/kbgfibschat.h
new file mode 100644
index 00000000..c3a1d670
--- /dev/null
+++ b/kbackgammon/engines/fibs/kbgfibschat.h
@@ -0,0 +1,273 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+#ifndef __KBGCHAT_H
+#define __KBGCHAT_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <kchat.h>
+
+class QString;
+class QPoint;
+class QListBox;
+class QListBoxItem;
+class QPopupMenu;
+
+class KTabCtl;
+class KAction;
+
+class KBgChatPrivate;
+
+/**
+ * Class of the FIBS Chat Windows
+ *
+ * This class inherits from KChat and represents a widget for a chat
+ * window. It has rich text entries and supports a powerful context
+ * menu.
+ *
+ * @short An extension of the KGame chat window for the FIBS engine
+ * @author Jens Hoefkens <jens@hoefkens.com>
+ *
+ */
+class KBgChat : public KChat
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * Constructor
+ */
+ KBgChat(QWidget *parent = 0, const char *name = 0);
+
+ /**
+ * Destructor
+ */
+ virtual ~KBgChat();
+
+public slots:
+
+ /**
+ * Catch the RMB signal to display a context menu at p. The
+ * menu shows entries specific to the selected item i.
+ */
+ void contextMenu(QListBoxItem *i, const QPoint &p);
+
+ /**
+ * Add chat window specific pages to the setup dialog
+ */
+ void getSetupPages(KTabCtl *nb, int space);
+
+ /**
+ * Save and apply the changes made in the setup dialog
+ */
+ void setupOk();
+
+ /**
+ * Do not save any of the changes made in the setup dialog
+ */
+ void setupCancel();
+
+ /**
+ * Load default values from the setup dialog
+ */
+ void setupDefault();
+
+ /**
+ * Player name has logges out. Remove name from the chat
+ * window combo box if necessary.
+ */
+ void deletePlayer(const QString &name);
+
+ /**
+ * Process and append msg to the text.
+ */
+ void handleData(const QString &msg);
+
+ /**
+ * Restore previously saved setting or provides defaults
+ */
+ void readConfig();
+
+ /**
+ * Save current settings
+ */
+ void saveConfig();
+
+ /**
+ * Set the opponents name and select whisper
+ */
+ void startGame(const QString &name);
+
+ /**
+ * Game is over. We won (or not) and have been playing (or not)
+ */
+ void endGame();
+
+ /**
+ * Start talking to name
+ */
+ void fibsTalk(const QString &name);
+
+signals:
+
+ /**
+ * Emits a string that can be sent to the server
+ */
+ void fibsCommand(const QString &cmd);
+
+ /**
+ * Request an invitation of player
+ */
+ void fibsRequestInvitation(const QString &player);
+
+ /**
+ * Text of a personal message
+ */
+ void personalMessage(const QString &msg);
+
+ /**
+ * Dialog is visible or not
+ */
+ void windowVisible(bool v);
+
+protected:
+
+ /**
+ * Catch show events, so the engine's menu can be updated.
+ */
+ virtual void showEvent(QShowEvent *e);
+
+ /**
+ * Catch hide events, so the engine's menu can be updated.
+ */
+ virtual void hideEvent(QHideEvent *e);
+
+ /**
+ * Create a custom ListBoxItem that contains a formated string
+ * for the chat window.
+ */
+ virtual QListBoxItem* layoutMessage(const QString& fromName, const QString& text);
+
+protected slots:
+
+ /**
+ * Invite the selected player using the dialog
+ */
+ void slotInviteD();
+
+ /**
+ * Invite the selected player to resume a match
+ */
+ void slotInviteR();
+
+ /**
+ * Invite the selected player to an unlimited match
+ */
+ void slotInviteU();
+
+ /**
+ * Invite the selected player to a 1 point match
+ */
+ void slotInvite1();
+
+ /**
+ * Invite the selected player to a 2 point match
+ */
+ void slotInvite2();
+
+ /**
+ * Invite the selected player to a 3 point match
+ */
+ void slotInvite3();
+
+ /**
+ * Invite the selected player to a 4 point match
+ */
+ void slotInvite4();
+
+ /**
+ * Invite the selected player to a 5 point match
+ */
+ void slotInvite5();
+
+ /**
+ * Invite the selected player to a 6 point match
+ */
+ void slotInvite6();
+
+ /**
+ * Invite the selected player to a 7 point match
+ */
+ void slotInvite7();
+
+ /**
+ * Request information on the selected player
+ */
+ void slotInquire();
+
+ /**
+ * Copy the selected line to the clipboard
+ */
+ void slotCopy();
+
+ /**
+ * Talk to the selected player
+ */
+ void slotTalk();
+
+ /**
+ * Add the selected player to the gag list
+ */
+ void slotGag();
+
+ /**
+ * Remove the selected player from the gag list
+ */
+ void slotUngag();
+
+ /**
+ * Clear the gag list
+ */
+ void slotCleargag();
+
+ /**
+ * Toggle everybody silent
+ */
+ void slotSilent();
+
+ /**
+ * Slot for return pressed. Time to send the text to FIBS.
+ */
+ void handleCommand(int id, const QString& msg);
+
+private:
+
+ KBgChatPrivate *d;
+
+};
+
+#endif // __KBGCHAT_H
diff --git a/kbackgammon/engines/fibs/kbginvite.cpp b/kbackgammon/engines/fibs/kbginvite.cpp
new file mode 100644
index 00000000..cb455f0a
--- /dev/null
+++ b/kbackgammon/engines/fibs/kbginvite.cpp
@@ -0,0 +1,185 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+#include "kbginvite.h"
+#include "kbginvite.moc"
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qframe.h>
+#include <qspinbox.h>
+#include <qstring.h>
+
+#include <klocale.h>
+#include <klineedit.h>
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+
+class KBgInvitePrivate {
+
+public:
+
+ KLineEdit *mLe;
+ QSpinBox *mSb;
+ QPushButton *mInvite, *mResume, *mUnlimited, *mCancel, *mClose;
+
+};
+
+/*
+ * Constructor is quite simple - most positioning is left to
+ * the toolkit.
+ */
+KBgInvite::KBgInvite(const char *name)
+ : KDialog(0, name, false)
+{
+ setCaption(i18n("Invite Players"));
+
+ d = new KBgInvitePrivate();
+
+ QLabel *info = new QLabel(this);
+
+ d->mLe = new KLineEdit(this, "invitation dialog");
+ d->mSb = new QSpinBox(1, 999, 1, this, "spin box");
+
+ d->mInvite = new QPushButton(i18n("&Invite"), this);
+ d->mResume = new QPushButton(i18n("&Resume"), this);
+ d->mUnlimited = new QPushButton(i18n("&Unlimited"), this);
+
+ d->mClose = new KPushButton(KStdGuiItem::close(), this);
+ d->mCancel = new KPushButton(KStdGuiItem::clear(), this);
+
+ info->setText(i18n("Type the name of the player you want to invite in the first entry\n"
+ "field and select the desired match length in the spin box."));
+
+ QFrame *hLine = new QFrame(this);
+ hLine->setFrameStyle(QFrame::Sunken|QFrame::HLine);
+
+ /*
+ * Set up layouts
+ */
+ QBoxLayout *vbox = new QVBoxLayout(this);
+
+ QBoxLayout *hbox_1 = new QHBoxLayout(vbox);
+ QBoxLayout *hbox_2 = new QHBoxLayout(vbox);
+ QBoxLayout *hbox_3 = new QHBoxLayout(vbox);
+ QBoxLayout *hbox_4 = new QHBoxLayout(vbox);
+ QBoxLayout *hbox_5 = new QHBoxLayout(vbox);
+
+ hbox_1->addWidget(info);
+
+ hbox_2->addWidget(d->mLe);
+ hbox_2->addWidget(d->mSb);
+
+ hbox_3->addWidget(hLine);
+
+ hbox_4->addWidget(d->mInvite);
+ hbox_4->addWidget(d->mResume);
+ hbox_4->addWidget(d->mUnlimited);
+
+ hbox_5->addWidget(d->mClose);
+ hbox_5->addWidget(d->mCancel);
+
+ /*
+ * Adjust widget sizes and resize the dialog
+ */
+ KDialog::resizeLayout(this, marginHint(), spacingHint());
+ setMinimumSize(childrenRect().size());
+ vbox->activate();
+ resize(minimumSize());
+
+ /*
+ * Set focus and default buttons
+ */
+ d->mInvite->setDefault(true);
+ d->mInvite->setAutoDefault(true);
+ d->mLe->setFocus();
+
+ /*
+ * Connect the buttons
+ */
+ connect(d->mUnlimited, SIGNAL(clicked()), SLOT(unlimitedClicked()));
+ connect(d->mResume, SIGNAL(clicked()), SLOT(resumeClicked()));
+ connect(d->mInvite, SIGNAL(clicked()), SLOT(inviteClicked()));
+ connect(d->mClose, SIGNAL(clicked()), SLOT(hide()));
+ connect(d->mCancel, SIGNAL(clicked()), SLOT(cancelClicked()));
+}
+
+/*
+ * Destructor
+ */
+KBgInvite::~KBgInvite()
+{
+ delete d;
+}
+
+/*
+ * After hiding, we tell our creator that we are ready to die.
+ */
+void KBgInvite::hide()
+{
+ emit dialogDone();
+}
+
+/*
+ * Set player name
+ */
+void KBgInvite::setPlayer(const QString &player)
+{
+ d->mLe->setText(player);
+}
+
+/*
+ * Invitation with number
+ */
+void KBgInvite::inviteClicked()
+{
+ QString tmp;
+ emit inviteCommand(QString("invite ") + d->mLe->text() + " " + tmp.setNum(d->mSb->value()));
+}
+
+/*
+ * Invitation for unlimited match
+ */
+void KBgInvite::unlimitedClicked()
+{
+ emit inviteCommand(QString("invite ") + d->mLe->text() + " unlimited");
+}
+
+/*
+ * Resume a game
+ */
+void KBgInvite::resumeClicked()
+{
+ emit inviteCommand(QString("invite ") + d->mLe->text());
+}
+
+/*
+ * Slot for Cancel. clear everything to default.
+ */
+void KBgInvite::cancelClicked()
+{
+ d->mSb->setValue(1);
+ d->mLe->clear();
+}
+
+// EOF
diff --git a/kbackgammon/engines/fibs/kbginvite.h b/kbackgammon/engines/fibs/kbginvite.h
new file mode 100644
index 00000000..992ee445
--- /dev/null
+++ b/kbackgammon/engines/fibs/kbginvite.h
@@ -0,0 +1,113 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+#ifndef __KBGINVITE_H
+#define __KBGINVITE_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <kdialog.h>
+
+class KBgInvitePrivate;
+
+/**
+ *
+ * This class implements a dialog for inviting players for games. It
+ * is quite simple (but follows the default style guide). The dialog
+ * offers specific numbers, unlimited and resume as invitation
+ * options.
+ *
+ * @short Simple dialog that allows to invite somebody on FIBS
+ * @author Jens Hoefkens <jens@hoefkens.com>
+ *
+ */
+class KBgInvite : public KDialog
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * Constructor
+ */
+ KBgInvite(const char *name = 0);
+
+ /**
+ * Destructor
+ */
+ virtual ~KBgInvite();
+
+public slots:
+
+ /**
+ * After hiding, we tell our creator that we are ready to die.
+ */
+ virtual void hide();
+
+ /**
+ * Set the name of the player in the line editor
+ */
+ void setPlayer(const QString &name);
+
+protected slots:
+
+ /**
+ * Emits the FIBS invitation command if the Ok button was clicked.
+ */
+ void inviteClicked();
+
+ /**
+ * Ask FIBS to resume a match
+ */
+ void resumeClicked();
+
+ /**
+ * Ask FIBS for an unlimited match
+ */
+ void unlimitedClicked();
+
+ /**
+ * Clear the entry field
+ */
+ void cancelClicked();
+
+signals:
+
+ /**
+ * Emits the text of an invitation
+ */
+ void inviteCommand(const QString &cmd);
+
+ /**
+ * Delete the dialog after it is closed.
+ */
+ void dialogDone();
+
+private:
+
+ KBgInvitePrivate *d;
+};
+
+#endif // __KBGINVITE_H
diff --git a/kbackgammon/engines/fibs/kplayerlist.cpp b/kbackgammon/engines/fibs/kplayerlist.cpp
new file mode 100644
index 00000000..102c354d
--- /dev/null
+++ b/kbackgammon/engines/fibs/kplayerlist.cpp
@@ -0,0 +1,902 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+#include "kplayerlist.moc"
+#include "kplayerlist.h"
+
+#include <qlayout.h>
+#include <qiconset.h>
+#include <qgroupbox.h>
+#include <qpopupmenu.h>
+#include <qcheckbox.h>
+#include <qlistview.h>
+#include <qwhatsthis.h>
+#include <qdatetime.h>
+#include <qlabel.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kaction.h>
+#include <kstdaction.h>
+#include <ktabctl.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+
+#include <string.h>
+#include <stdio.h>
+
+#include "kbgfibs.h"
+#include "version.h"
+
+
+/*
+ * Simple container for information on columns of the list view.
+ *
+ * index : the internal index in the list
+ * width : width of the column in pixel
+ * show : whether the column is visible
+ * cb : check box for the setup dialog
+ */
+class KFibsPlayerListCI {
+
+public:
+
+ int index, width;
+ bool show;
+ QCheckBox *cb;
+ QString key, name;
+
+};
+
+/*
+ * Extension of the QListViewItem class that has a custom key function
+ * that can deal with the different items of the player list.
+ */
+class KFibsPlayerListLVI : public KListViewItem {
+
+public:
+
+ /*
+ * Constructor
+ */
+ KFibsPlayerListLVI(KFibsPlayerList *parent) : KListViewItem(parent) { _plist = parent; }
+
+ /*
+ * Destructor
+ */
+ virtual ~KFibsPlayerListLVI() {}
+
+ /*
+ * Overloaded key function for sorting
+ */
+ virtual QString key(int col, bool) const
+ {
+ int real_col = _plist->cIndex(col);
+
+ QString s = text(col);
+
+ switch (real_col) {
+ case KFibsPlayerList::Player:
+ case KFibsPlayerList::Opponent:
+ case KFibsPlayerList::Watches:
+ case KFibsPlayerList::Client:
+ case KFibsPlayerList::Email:
+ case KFibsPlayerList::Status:
+ case KFibsPlayerList::Host:
+ s = s.lower();
+ break;
+ case KFibsPlayerList::Idle:
+ case KFibsPlayerList::Experience:
+ s.sprintf("%08d", s.toInt());
+ break;
+ case KFibsPlayerList::Rating:
+ s.sprintf("%08d", (int)(1000*s.toDouble()));
+ break;
+ case KFibsPlayerList::Time:
+ s = s.lower();
+ break;
+ default:
+ kdDebug(10500) << "KFibsPlayerListLVI::key(): illegal column" << endl;
+ break;
+ }
+ return s;
+ }
+
+private:
+
+ KFibsPlayerList *_plist;
+
+};
+
+/*
+ * Private data of the player list
+ */
+class KFibsPlayerListPrivate {
+
+public:
+
+ /*
+ * Named constants for the popup menu actions
+ */
+ enum MenuID {Info, Talk, Mail, InviteD, Invite1, Invite2, Invite3, Invite4,
+ Invite5, Invite6, Invite7, InviteR, InviteU,
+ Look, Watch, Unwatch, BlindAct, Update, Reload, Close, ActionEnd};
+
+ /*
+ * Various actions for the context menu
+ */
+ KAction *mAct[ActionEnd];
+
+ /*
+ * All relevant information on the columns
+ */
+ KFibsPlayerListCI *mCol[KFibsPlayerList::LVEnd];
+
+ /*
+ * Context menus for player related commands
+ */
+ QPopupMenu *mPm[2];
+
+ /*
+ * ID of the invite menu in the context menu
+ */
+ int mInID;
+
+ /*
+ * Are we watching?
+ */
+ bool mWatch;
+
+ /*
+ * count similar clients - KFibs & kbackgammon
+ */
+ int mCount[2];
+
+ /*
+ * Short abbreviations for Blind, Ready, and Away.
+ */
+ QString mAbrv[KFibsPlayerList::MaxStatus];
+
+ /*
+ * Name of the last selected player - for internal purposes
+ */
+ QString mUser;
+
+ /*
+ * Our own name
+ */
+ QString mName;
+
+ /*
+ * Email address of the last selected player - for internal purposes
+ */
+ QString mMail;
+
+};
+
+
+// == constructor, destructor and setup ========================================
+
+/*
+ * Construct the playerlist and do some initial setup
+ */
+KFibsPlayerList::KFibsPlayerList(QWidget *parent, const char *name)
+ : KListView(parent, name)
+{
+ d = new KFibsPlayerListPrivate();
+ KActionCollection* actions = new KActionCollection(this);
+
+ /*
+ * Allocate the column information
+ */
+ for (int i = 0; i < LVEnd; i++)
+ d->mCol[i] = new KFibsPlayerListCI();
+
+ /*
+ * Initialize variables
+ */
+ d->mCol[Player]->name = i18n("Player");
+ d->mCol[Opponent]->name = i18n("Opponent");
+ d->mCol[Watches]->name = i18n("Watches");
+ d->mCol[Status]->name = i18n("Status");
+ d->mCol[Rating]->name = i18n("Rating");
+ d->mCol[Experience]->name = i18n("Exp.");
+ d->mCol[Idle]->name = i18n("Idle");
+ d->mCol[Time]->name = i18n("Time");
+ d->mCol[Host]->name = i18n("Host name");
+ d->mCol[Client]->name = i18n("Client");
+ d->mCol[Email]->name = i18n("Email");
+
+ // These strings shouldn't be translated!!
+ d->mCol[Player]->key = "player";
+ d->mCol[Opponent]->key = "opponent";
+ d->mCol[Watches]->key = "watches";
+ d->mCol[Status]->key = "status";
+ d->mCol[Rating]->key = "rating";
+ d->mCol[Experience]->key = "experience";
+ d->mCol[Idle]->key = "idle";
+ d->mCol[Time]->key = "time";
+ d->mCol[Host]->key = "hostname";
+ d->mCol[Client]->key = "client";
+ d->mCol[Email]->key = "email";
+
+ d->mCount[0] = d->mCount[1] = 0;
+
+ d->mAbrv[Blind] = i18n("abreviate blind", "B");
+ d->mAbrv[Away ] = i18n("abreviate away", "A");
+ d->mAbrv[Ready] = i18n("abreviate ready", "R");
+
+ d->mName = QString::null;
+
+ d->mWatch = false;
+
+ /*
+ * Get a sane caption, initialize some eye candy and read the
+ * configuration - needed for the column information.
+ */
+ updateCaption();
+ setIcon(kapp->miniIcon());
+ QWhatsThis::add(this, i18n("This window contains the player list. It shows "
+ "all players that are currently logged into FIBS."
+ "Use the right mouse button to get a context "
+ "menu with helpful information and commands."));
+
+ readColumns();
+
+ /*
+ * Put the columns into the list view
+ */
+ for (int i = 0; i < LVEnd; i++) {
+ if (d->mCol[i]->show) {
+ d->mCol[i]->index = addColumn(d->mCol[i]->name, d->mCol[i]->width);
+ if (i == Experience || i == Rating || i == Time || i == Idle)
+ setColumnAlignment(d->mCol[i]->index, AlignRight);
+ } else {
+ d->mCol[i]->index = -1;
+ }
+ }
+ setAllColumnsShowFocus(true);
+
+ /*
+ * Create context menus
+ */
+ d->mPm[0] = new QPopupMenu();
+ d->mPm[1] = new QPopupMenu();
+
+ /*
+ * Create the whole set of actions
+ */
+ d->mAct[KFibsPlayerListPrivate::Info] = new KAction(i18n("Info"),
+ QIconSet(kapp->iconLoader()->loadIcon
+ ("help.xpm", KIcon::Small)),
+ 0, this, SLOT(slotInfo()), actions);
+ d->mAct[KFibsPlayerListPrivate::Talk] = new KAction(i18n("Talk"),
+ QIconSet(kapp->iconLoader()->loadIcon
+ (PROG_NAME "-chat.png", KIcon::Small)),
+ 0, this, SLOT(slotTalk()), actions);
+
+ d->mAct[KFibsPlayerListPrivate::Look] = new KAction(i18n("Look"), 0, this, SLOT(slotLook()), actions);
+ d->mAct[KFibsPlayerListPrivate::Watch] = new KAction(i18n("Watch"), 0, this, SLOT(slotWatch()), actions);
+ d->mAct[KFibsPlayerListPrivate::Unwatch] = new KAction(i18n("Unwatch"), 0, this, SLOT(slotUnwatch()),actions);
+ d->mAct[KFibsPlayerListPrivate::BlindAct] = new KAction(i18n("Blind"), 0, this, SLOT(slotBlind()), actions);
+ d->mAct[KFibsPlayerListPrivate::Update] = new KAction(i18n("Update"), 0, this, SLOT(slotUpdate()), actions);
+
+ d->mAct[KFibsPlayerListPrivate::Reload] = KStdAction::redisplay(this, SLOT(slotReload()), actions);
+ d->mAct[KFibsPlayerListPrivate::Mail] = KStdAction::mail(this, SLOT(slotMail()), actions);
+ d->mAct[KFibsPlayerListPrivate::Close] = KStdAction::close(this, SLOT(hide()), actions);
+
+ d->mAct[KFibsPlayerListPrivate::InviteD] = new KAction(i18n("Use Dialog"), 0, this,
+ SLOT(slotInviteD()), actions);
+ d->mAct[KFibsPlayerListPrivate::Invite1] = new KAction(i18n("1 Point Match"), 0, this,
+ SLOT(slotInvite1()), actions);
+ d->mAct[KFibsPlayerListPrivate::Invite2] = new KAction(i18n("2 Point Match"), 0, this,
+ SLOT(slotInvite2()), actions);
+ d->mAct[KFibsPlayerListPrivate::Invite3] = new KAction(i18n("3 Point Match"), 0, this,
+ SLOT(slotInvite3()), actions);
+ d->mAct[KFibsPlayerListPrivate::Invite4] = new KAction(i18n("4 Point Match"), 0, this,
+ SLOT(slotInvite4()), actions);
+ d->mAct[KFibsPlayerListPrivate::Invite5] = new KAction(i18n("5 Point Match"), 0, this,
+ SLOT(slotInvite5()), actions);
+ d->mAct[KFibsPlayerListPrivate::Invite6] = new KAction(i18n("6 Point Match"), 0, this,
+ SLOT(slotInvite6()), actions);
+ d->mAct[KFibsPlayerListPrivate::Invite7] = new KAction(i18n("7 Point Match"), 0, this,
+ SLOT(slotInvite7()), actions);
+ d->mAct[KFibsPlayerListPrivate::InviteU] = new KAction(i18n("Unlimited"), 0, this,
+ SLOT(slotInviteU()), actions);
+ d->mAct[KFibsPlayerListPrivate::InviteR] = new KAction(i18n("Resume"), 0, this,
+ SLOT(slotInviteR()), actions);
+
+ /*
+ * Fill normal context menu
+ */
+ d->mAct[KFibsPlayerListPrivate::Info]->plug(d->mPm[0]);
+ d->mAct[KFibsPlayerListPrivate::Talk]->plug(d->mPm[0]);
+ d->mAct[KFibsPlayerListPrivate::Mail]->plug(d->mPm[0]);
+ d->mPm[0]->insertSeparator();
+ d->mInID = d->mPm[0]->insertItem(i18n("Invite"), d->mPm[1]); // save ID for later
+ d->mAct[KFibsPlayerListPrivate::Look ]->plug(d->mPm[0]);
+ d->mAct[KFibsPlayerListPrivate::Watch ]->plug(d->mPm[0]);
+ d->mAct[KFibsPlayerListPrivate::Unwatch ]->plug(d->mPm[0]);
+ d->mAct[KFibsPlayerListPrivate::BlindAct]->plug(d->mPm[0]);
+ d->mPm[0]->insertSeparator();
+ d->mAct[KFibsPlayerListPrivate::Update]->plug(d->mPm[0]);
+ d->mAct[KFibsPlayerListPrivate::Reload]->plug(d->mPm[0]);
+ d->mPm[0]->insertSeparator();
+ d->mAct[KFibsPlayerListPrivate::Close]->plug(d->mPm[0]);
+
+ /*
+ * Fill the invitation menu
+ */
+ d->mAct[KFibsPlayerListPrivate::InviteD]->plug(d->mPm[1]);
+ d->mPm[1]->insertSeparator();
+ d->mAct[KFibsPlayerListPrivate::Invite1]->plug(d->mPm[1]);
+ d->mAct[KFibsPlayerListPrivate::Invite2]->plug(d->mPm[1]);
+ d->mAct[KFibsPlayerListPrivate::Invite3]->plug(d->mPm[1]);
+ d->mAct[KFibsPlayerListPrivate::Invite4]->plug(d->mPm[1]);
+ d->mAct[KFibsPlayerListPrivate::Invite5]->plug(d->mPm[1]);
+ d->mAct[KFibsPlayerListPrivate::Invite6]->plug(d->mPm[1]);
+ d->mAct[KFibsPlayerListPrivate::Invite7]->plug(d->mPm[1]);
+ d->mPm[1]->insertSeparator();
+ d->mAct[KFibsPlayerListPrivate::InviteU]->plug(d->mPm[1]);
+ d->mAct[KFibsPlayerListPrivate::InviteR]->plug(d->mPm[1]);
+
+ /*
+ * Right mouse button gets context menu, double click gets player info
+ */
+ connect(this, SIGNAL(contextMenu(KListView *, QListViewItem *, const QPoint &)),
+ this, SLOT(showContextMenu(KListView *, QListViewItem *, const QPoint &)));
+ connect(this, SIGNAL(doubleClicked(QListViewItem *, const QPoint &, int)),
+ this, SLOT(getPlayerInfo(QListViewItem *, const QPoint &, int)));
+}
+
+/*
+ * Destructor deletes members
+ */
+KFibsPlayerList::~KFibsPlayerList()
+{
+ for (int i = 0; i < LVEnd; i++)
+ delete d->mCol[i];
+ delete d->mPm[0];
+ delete d->mPm[1];
+ delete d;
+}
+
+
+// == settings and config ======================================================
+
+/*
+ * Called when the setup dialog is positively closed
+ */
+void KFibsPlayerList::setupOk()
+{
+ int i;
+ bool change = false;
+
+ for (i = 1; i < LVEnd; i++)
+ change |= (d->mCol[i]->cb->isChecked() != d->mCol[i]->show);
+
+ /*
+ * Only juggle with the columns if something changed
+ */
+ if (change) {
+
+ /*
+ * It's important to remove the columns in reverse order
+ */
+ for (i = LVEnd-1; i > 0; i--)
+ if (d->mCol[i]->show)
+ removeColumn(d->mCol[i]->index);
+
+ /*
+ * Now add all columns that are selected
+ */
+ for (i = 1; i < LVEnd; i++) {
+ if ((d->mCol[i]->show = d->mCol[i]->cb->isChecked())) {
+ d->mCol[i]->index = addColumn(d->mCol[i]->name, d->mCol[i]->width);
+ if (i == Experience || i == Rating || i == Time || i == Idle)
+ setColumnAlignment(d->mCol[i]->index, AlignRight);
+ } else {
+ d->mCol[i]->index = -1;
+ }
+ }
+
+ /*
+ * Reload the list
+ */
+ slotReload();
+ }
+
+ /*
+ * store the new settings
+ */
+ saveConfig();
+
+}
+
+/*
+ * Setup dialog page of the player list - allow the user to select the
+ * columns to show
+ */
+void KFibsPlayerList::getSetupPages(KTabCtl *nb, int space)
+{
+ int i;
+
+ /*
+ * Main Widget
+ */
+ QWidget *w = new QWidget(nb);
+ QGridLayout *gl = new QGridLayout(w, 2, 1, space);
+
+ /*
+ * Label
+ */
+ QGroupBox *gbl = new QGroupBox(w);
+ gbl->setTitle(i18n("Column Selection"));
+
+ gl->addWidget(gbl, 0, 0);
+
+ /*
+ * Note that the first column (Player == 0) is always there
+ */
+ QLabel *lb = new QLabel(i18n("Select all the columns that you would\n"
+ "like to be shown in the player list."), gbl);
+
+ for (i = 1; i < LVEnd; i++) {
+ d->mCol[i]->cb = new QCheckBox(d->mCol[i]->name, gbl);
+ d->mCol[i]->cb->setChecked(d->mCol[i]->show);
+ }
+
+ gl = new QGridLayout(gbl, LVEnd, 2, 20);
+ gl->addWidget(lb, 0, 0);
+
+ // two column layout....
+ for (i = 1; i < LVEnd/2; i++) {
+ gl->addWidget(d->mCol[2*i-1]->cb, i, 0);
+ gl->addWidget(d->mCol[2*i ]->cb, i, 1);
+ }
+ gl->addWidget(d->mCol[2*i-1]->cb, i, 0);
+ if (2*i < LVEnd)
+ gl->addWidget(d->mCol[2*i]->cb, i, 1);
+
+ /*
+ * put in the page and connect
+ */
+ nb->addTab(w, i18n("&Playerlist"));
+
+ connect(nb, SIGNAL(applyButtonPressed()), this, SLOT(setupOk()));
+}
+
+/*
+ * Nothing to cancel/undo
+ */
+void KFibsPlayerList::setupCancel()
+{
+ // do nothing
+}
+
+/*
+ * By default all entries are checked
+ */
+void KFibsPlayerList::setupDefault()
+{
+ for (int i = 0; i < LVEnd; i++)
+ d->mCol[i]->cb->setChecked(true);
+}
+
+/*
+ * Restore the columns
+ */
+void KFibsPlayerList::readColumns()
+{
+ KConfig* config = kapp->config();
+ config->setGroup(name());
+
+ for (int i = 0; i < LVEnd; i++) {
+ d->mCol[i]->show = config->readBoolEntry("col-" + d->mCol[i]->key, true);
+ d->mCol[i]->width = config->readNumEntry("col-w-" + d->mCol[i]->key, -1);
+ }
+}
+
+/*
+ * Restore the saved settings
+ */
+void KFibsPlayerList::readConfig()
+{
+ KConfig* config = kapp->config();
+ config->setGroup(name());
+
+ QPoint pos, defpos(10, 10);
+ pos = config->readPointEntry("ori", &defpos);
+ setGeometry(pos.x(), pos.y(), config->readNumEntry("wdt",460),
+ config->readNumEntry("hgt",190));
+
+ (config->readBoolEntry("vis", false)) ? show() : hide();
+
+ readColumns();
+}
+
+/*
+ * Save current settings
+ */
+void KFibsPlayerList::saveConfig()
+{
+ KConfig* config = kapp->config();
+ config->setGroup(name());
+
+ config->writeEntry("ori", pos());
+ config->writeEntry("hgt", height());
+ config->writeEntry("wdt", width());
+
+ config->writeEntry("vis", isVisible());
+
+ for (int i = 0; i < LVEnd; i++) {
+ config->writeEntry("col-" + d->mCol[i]->key, d->mCol[i]->show);
+ config->writeEntry("col-w-" + d->mCol[i]->key,
+ (d->mCol[i]->show) ? columnWidth(d->mCol[i]->index) : -1);
+ }
+}
+
+
+// == popup menu slots and functions ===========================================
+
+/*
+ * Save selected player, update the menu entries and show the popup menu
+ */
+void KFibsPlayerList::showContextMenu(KListView *, QListViewItem *i, const QPoint &p)
+{
+ /*
+ * Get the name of the selected player
+ */
+ d->mUser = (i ? i->text(Player) : QString::null);
+
+ d->mAct[KFibsPlayerListPrivate::Info ]->setText(i18n("Info on %1" ).arg(d->mUser));
+ d->mAct[KFibsPlayerListPrivate::Talk ]->setText(i18n("Talk to %1" ).arg(d->mUser));
+ d->mAct[KFibsPlayerListPrivate::Mail ]->setText(i18n("Email to %1").arg(d->mUser));
+ d->mAct[KFibsPlayerListPrivate::Look ]->setText(i18n("Look at %1" ).arg(d->mUser));
+ d->mAct[KFibsPlayerListPrivate::Watch ]->setText(i18n("Watch %1" ).arg(d->mUser));
+ d->mAct[KFibsPlayerListPrivate::Update]->setText(i18n("Update %1" ).arg(d->mUser));
+
+ d->mAct[KFibsPlayerListPrivate::Info ]->setEnabled(i);
+ d->mAct[KFibsPlayerListPrivate::Talk ]->setEnabled(i);
+ d->mAct[KFibsPlayerListPrivate::Mail ]->setEnabled(i);
+ d->mAct[KFibsPlayerListPrivate::Look ]->setEnabled(i);
+ d->mAct[KFibsPlayerListPrivate::Watch ]->setEnabled(i);
+ d->mAct[KFibsPlayerListPrivate::Update ]->setEnabled(i);
+ d->mAct[KFibsPlayerListPrivate::BlindAct]->setEnabled(i);
+
+ d->mAct[KFibsPlayerListPrivate::Unwatch]->setEnabled(d->mWatch);
+
+ d->mPm[0]->setItemEnabled(d->mInID, i && d->mName != d->mUser);
+ d->mPm[0]->changeItem(d->mInID, i18n("Invite %1").arg(d->mUser));
+
+ d->mMail = (i && d->mCol[Email]->show ? i->text(d->mCol[Email]->index) : QString::null);
+ d->mAct[KFibsPlayerListPrivate::Mail]->setEnabled(!d->mMail.isEmpty());
+
+ if (i && d->mCol[Status]->show)
+ d->mAct[KFibsPlayerListPrivate::BlindAct]->setText
+ ((i->text(d->mCol[Status]->index).contains(d->mAbrv[Blind])) ?
+ i18n("Unblind %1").arg(d->mUser) : i18n("Blind %1").arg(d->mUser));
+ else
+ d->mAct[KFibsPlayerListPrivate::BlindAct]->setText(i18n("Blind"));
+
+ // show the menu
+ d->mPm[0]->popup(p);
+}
+
+/*
+ * Reload the entire list
+ */
+void KFibsPlayerList::slotReload()
+{
+ emit fibsCommand("rawwho");
+ clear();
+}
+
+/*
+ * Stop watching
+ */
+void KFibsPlayerList::slotUnwatch()
+{
+ emit fibsCommand("unwatch");
+}
+
+/*
+ * Blind/Unblind user
+ */
+void KFibsPlayerList::slotBlind()
+{
+ emit fibsCommand("blind " + d->mUser);
+}
+
+/*
+ * Start talking to user
+ */
+void KFibsPlayerList::slotTalk()
+{
+ emit fibsTalk(d->mUser);
+}
+
+/*
+ * Request information on user
+ */
+void KFibsPlayerList::slotInfo()
+{
+ emit fibsCommand("whois " + d->mUser);
+}
+
+/*
+ * Look at user
+ */
+void KFibsPlayerList::slotLook()
+{
+ emit fibsCommand("look " + d->mUser);
+}
+
+/*
+ * Send an email to player user at address email
+ */
+void KFibsPlayerList::slotMail()
+{
+ kapp->invokeMailer(d->mMail, QString::null);
+}
+
+/*
+ * Request a new entry for user
+ */
+void KFibsPlayerList::slotUpdate()
+{
+ emit fibsCommand("rawwho " + d->mUser);
+}
+
+/*
+ * Watch user and get an updated board
+ */
+void KFibsPlayerList::slotWatch()
+{
+ emit fibsCommand("watch " + d->mUser);
+ emit fibsCommand("board");
+}
+
+/*
+ * Request information about the selected user
+ */
+void KFibsPlayerList::getPlayerInfo(QListViewItem *i, const QPoint &, int col)
+{
+ int num = cIndex(col);
+ if (col < 0 || num < 0 || num > 2 || i->text(num).isEmpty())
+ num = 0;
+ emit fibsCommand("whois " + i->text(num));
+}
+
+/*
+ * Invite the selected user.
+ */
+void KFibsPlayerList::slotInviteD()
+{
+ emit fibsInvite(d->mUser);
+}
+void KFibsPlayerList::slotInvite1() { emit fibsCommand("invite " + d->mUser + " 1"); }
+void KFibsPlayerList::slotInvite2() { emit fibsCommand("invite " + d->mUser + " 2"); }
+void KFibsPlayerList::slotInvite3() { emit fibsCommand("invite " + d->mUser + " 3"); }
+void KFibsPlayerList::slotInvite4() { emit fibsCommand("invite " + d->mUser + " 4"); }
+void KFibsPlayerList::slotInvite5() { emit fibsCommand("invite " + d->mUser + " 5"); }
+void KFibsPlayerList::slotInvite6() { emit fibsCommand("invite " + d->mUser + " 6"); }
+void KFibsPlayerList::slotInvite7() { emit fibsCommand("invite " + d->mUser + " 7"); }
+
+void KFibsPlayerList::slotInviteU() { emit fibsCommand("invite " + d->mUser + " unlimited"); }
+void KFibsPlayerList::slotInviteR() { emit fibsCommand("invite " + d->mUser); }
+
+
+// == inserting and updating the list ==========================================
+
+/*
+ * Add or change the entry of player with the corresponding string
+ * from the server - rawwho
+ */
+void KFibsPlayerList::changePlayer(const QString &line)
+{
+ char entry[LVEnd][100];
+ char ready[2], away[2];
+ QListViewItem *i;
+ QDateTime fromEpoch;
+ QString str_entry[LVEnd], tmp;
+
+ entry[Status][0] = '\0';
+
+ // the line comes from FIBS and is 7 bit ASCII
+ sscanf(line.latin1(), "%99s %99s %99s %1s %1s %99s %99s %99s %99s %99s %99s %99s", entry[Player], entry[Opponent],
+ entry[Watches], ready, away, entry[Rating], entry[Experience], entry[Idle], entry[Time],
+ entry[Host], entry[Client], entry[Email]);
+
+ // convert time
+ tmp = entry[Time];
+ fromEpoch.setTime_t(tmp.toUInt());
+ strcpy(entry[Time], fromEpoch.toString().latin1());
+
+ // clear empty strings and copy
+ for (int j = 0; j < LVEnd; j++) {
+ if ((str_entry[j] = entry[j]) == "-")
+ str_entry[j] = "";
+ }
+ str_entry[Status].replace(Ready, 1, ready[0] == '0' ? "-" : d->mAbrv[Ready]);
+ str_entry[Status].replace(Away, 1, away [0] == '0' ? "-" : d->mAbrv[Away ]);
+ str_entry[Status].replace(Blind, 1, "-");
+
+ // disable drawing until the end of update
+ setUpdatesEnabled(false);
+
+ // try to find the item in the list
+ QListViewItemIterator it(this);
+ for ( ; it.current(); ++it) {
+ if (it.current()->text(0) == str_entry[Player]) {
+ i = it.current();
+ goto found;
+ }
+ }
+
+ // getting here means we have to create a new entry
+ i = new KFibsPlayerListLVI(this);
+
+ // count the KFibs and KBackgammon clients
+ if (str_entry[Client].contains("KFibs"))
+ d->mCount[0]++;
+ else if (str_entry[Client].contains(PROG_NAME))
+ d->mCount[1]++;
+
+ // new entry requires an update to the player count
+ updateCaption();
+
+ goto update;
+
+ found:
+
+ // getting here means the player is in the list - update private status
+ str_entry[Status].replace(Blind,1,i->text(Status).contains
+ (d->mAbrv[Blind]) ? d->mAbrv[Blind] : "-");
+
+ update:
+
+ for (int j = 0; j < LVEnd; j++) {
+ if (d->mCol[j]->show)
+ i->setText(d->mCol[j]->index, str_entry[j]);
+ }
+
+ // find out if we are watching somebody
+ if (d->mName == str_entry[Player])
+ d->mWatch = !str_entry[Watches].isEmpty();
+}
+
+/*
+ * Remove player from the list
+ */
+void KFibsPlayerList::deletePlayer(const QString &player)
+{
+ QListViewItemIterator it(this);
+ for ( ; it.current(); ++it) {
+ if (it.current()->text(0) == player) {
+ if (it.current()->text(Client).contains(PROG_NAME))
+ --d->mCount[1];
+ else if (it.current()->text(Client).contains("KFibs"))
+ --d->mCount[0];
+ delete it.current();
+ updateCaption();
+ return;
+ }
+ }
+}
+
+/*
+ * Set/Unset the status stat in the corresponding column of the list
+ */
+void KFibsPlayerList::changePlayerStatus(const QString &player, int stat, bool flag)
+{
+ QListViewItem *i = 0;
+
+ /*
+ * Find the correct line
+ */
+ QListViewItemIterator it(this);
+ for ( ; it.current(); ++it) {
+ if (it.current()->text(Player) == player) {
+ i = it.current();
+ break;
+ }
+ }
+ if (!i) return;
+
+ /*
+ * Update the status flag
+ */
+ i->setText(Status, i->text(Status).replace(stat, 1, (flag) ? d->mAbrv[stat] : "-"));
+}
+
+
+// == various slots and functions ==============================================
+
+/*
+ * Reverse column to index mapping. Return negative on error.
+ */
+int KFibsPlayerList::cIndex(int col)
+{
+ for (int i = 0; i < LVEnd; i++)
+ if (d->mCol[i]->index == col)
+ return i;
+ return -1;
+}
+
+/*
+ * Catch hide events, so the engine's menu can be update.
+ */
+void KFibsPlayerList::showEvent(QShowEvent *e)
+{
+ KListView::showEvent(e);
+ emit windowVisible(true);
+}
+
+/*
+ * Catch hide events, so the engine's menu can be update.
+ */
+void KFibsPlayerList::hideEvent(QHideEvent *e)
+{
+ emit windowVisible(false);
+ KListView::hideEvent(e);
+}
+
+/*
+ * Called at the end of updates to re-enable the UI
+ */
+void KFibsPlayerList::stopUpdate()
+{
+ setUpdatesEnabled(true);
+ triggerUpdate();
+}
+
+/*
+ * Knowing our own name allows us to disable certain menu entries for
+ * ourselves.
+ */
+void KFibsPlayerList::setName(const QString &name)
+{
+ d->mName = name;
+}
+
+/*
+ * Update the caption of the list by including the current client
+ * count
+ */
+void KFibsPlayerList::updateCaption()
+{
+ setCaption(i18n("Player List - %1 - %2/%3").arg(childCount()).arg(d->mCount[0]).arg(d->mCount[1]));
+}
+
+/*
+ * Clear the list and reset the client counters
+ */
+void KFibsPlayerList::clear()
+{
+ d->mCount[0] = 0;
+ d->mCount[1] = 0;
+ QListView::clear();
+}
+
+// EOF
diff --git a/kbackgammon/engines/fibs/kplayerlist.h b/kbackgammon/engines/fibs/kplayerlist.h
new file mode 100644
index 00000000..701f9ace
--- /dev/null
+++ b/kbackgammon/engines/fibs/kplayerlist.h
@@ -0,0 +1,298 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+#ifndef __KPLAYERLIST_H
+#define __KPLAYERLIST_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <klistview.h>
+
+class KTabCtl;
+class KFibsPlayerListPrivate;
+
+/**
+ *
+ * A class that keeps track of players on the server. The server is flooding
+ * us with user information. At any given time we are able to have an current
+ * list of all loged-in players and their status.
+ *
+ * @short The FIBS backgammon engine player list
+ * @author Jens Hoefkens <jens@hoefkens.com>
+ *
+ */
+class KFibsPlayerList : public KListView
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * Enumerate player status
+ */
+ enum PStatus {Ready, Away, Blind, MaxStatus};
+
+ /**
+ * Enumerate the different columns of the list
+ */
+ enum {Player, Opponent, Watches, Status, Rating, Experience,
+ Idle, Time, Host, Client, Email, LVEnd};
+
+ /**
+ * Constructor
+ */
+ KFibsPlayerList(QWidget *parent = 0, const char *name = 0);
+
+ /**
+ * Destructor
+ */
+ virtual ~KFibsPlayerList();
+
+ /**
+ * Clear the list and reset the client counter
+ */
+ virtual void clear();
+
+public slots:
+
+ /**
+ * Remove the player with the name given by the first word
+ */
+ void deletePlayer(const QString &player);
+
+ /**
+ * Change/Add the entry for the given player
+ */
+ void changePlayer(const QString &line);
+
+ /**
+ * Enables list redraws after an update
+ */
+ void stopUpdate();
+
+ /**
+ * Read the UI settings from disk
+ */
+ void readConfig();
+
+ /**
+ * Read the column info from disk
+ */
+ void readColumns();
+
+ /**
+ * Restore settings from previously stored settings
+ */
+ void saveConfig();
+
+ /**
+ * Change the status of a player
+ */
+ void changePlayerStatus(const QString &player, int stat, bool flag);
+
+ /**
+ * Fills the playerlist page into the notebook
+ */
+ virtual void getSetupPages(KTabCtl *nb, int space);
+
+ /**
+ * Save setting changes
+ */
+ void setupOk();
+
+ /**
+ * Setup changes have been cancelled
+ */
+ void setupCancel();
+
+ /**
+ * Set default settings
+ */
+ void setupDefault();
+
+ /**
+ * Set our own name. This allows us to special case the context
+ * menu.
+ */
+ void setName(const QString &name);
+
+ /**
+ * Return the column index
+ */
+ int cIndex(int col);
+
+protected:
+
+ /**
+ * Catch show events, so the engine's menu can be update.
+ */
+ virtual void showEvent(QShowEvent *e);
+
+ /**
+ * Catch hide events, so the engine's menu can be update.
+ */
+ virtual void hideEvent(QHideEvent *e);
+
+protected slots:
+
+ /**
+ * Double click handler, requests information on a player
+ */
+ void getPlayerInfo(QListViewItem *i, const QPoint &p, int col);
+
+ /**
+ * Display a popup menu for the current player
+ */
+ void showContextMenu(KListView *, QListViewItem *, const QPoint &);
+
+ /**
+ * Reload the whole list
+ */
+ void slotReload();
+
+ /**
+ * Upate the caption
+ */
+ void updateCaption();
+
+ /**
+ * Watch user
+ */
+ void slotWatch();
+
+ /**
+ * Update line of user
+ */
+ void slotUpdate();
+
+ /**
+ * Request information on user
+ */
+ void slotInfo();
+
+ /**
+ * Look at user
+ */
+ void slotLook();
+
+ /**
+ * Send an email to user
+ */
+ void slotMail();
+
+ /**
+ * Stop watching
+ */
+ void slotUnwatch();
+
+ /**
+ * Blind user
+ */
+ void slotBlind();
+
+ /**
+ * Talk to user
+ */
+ void slotTalk();
+
+ /**
+ * Invite using the dialog
+ */
+ void slotInviteD();
+
+ /**
+ * Invite to a 1 point match
+ */
+ void slotInvite1();
+
+ /**
+ * Invite to a 2 point match
+ */
+ void slotInvite2();
+
+ /**
+ * Invite to a 3 point match
+ */
+ void slotInvite3();
+
+ /**
+ * Invite to a 4 point match
+ */
+ void slotInvite4();
+
+ /**
+ * Invite to a 5 point match
+ */
+ void slotInvite5();
+
+ /**
+ * Invite to a 6 point match
+ */
+ void slotInvite6();
+
+ /**
+ * Invite to a 7 point match
+ */
+ void slotInvite7();
+
+ /**
+ * Invite to resume a saved match
+ */
+ void slotInviteR();
+
+ /**
+ * Invite to an unlimited match
+ */
+ void slotInviteU();
+
+signals:
+
+ /**
+ * Send a command to the server
+ */
+ void fibsCommand(const QString &);
+
+ /**
+ * Initiate an invitation of a player
+ */
+ void fibsInvite(const QString &);
+
+ /**
+ * Request talking to player user
+ */
+ void fibsTalk(const QString &);
+
+ /**
+ * Allow the engine's menu to be updated
+ */
+ void windowVisible(bool);
+
+private:
+
+ KFibsPlayerListPrivate *d;
+
+};
+
+#endif // __KPLAYERLIST_H
diff --git a/kbackgammon/engines/generic/Makefile.am b/kbackgammon/engines/generic/Makefile.am
new file mode 100644
index 00000000..da0f2fdb
--- /dev/null
+++ b/kbackgammon/engines/generic/Makefile.am
@@ -0,0 +1,8 @@
+noinst_LTLIBRARIES = libkbggeneric.la
+
+libkbggeneric_la_SOURCES = kbgengine.cpp
+
+INCLUDES= -I$(top_srcdir)/kbackgammon $(all_includes)
+
+METASOURCES = AUTO
+
diff --git a/kbackgammon/engines/generic/kbgengine.cpp b/kbackgammon/engines/generic/kbgengine.cpp
new file mode 100644
index 00000000..bbe528a6
--- /dev/null
+++ b/kbackgammon/engines/generic/kbgengine.cpp
@@ -0,0 +1,62 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+#include <qtimer.h>
+#include <qpopupmenu.h>
+
+#include <kdialogbase.h>
+
+#include <kbgengine.moc>
+#include "kbgengine.h"
+
+
+/*
+ * Constructor initializes the QObject
+ */
+KBgEngine::KBgEngine(QWidget *parent, QString *name, QPopupMenu *pmenu)
+ : QObject(parent, name->local8Bit())
+{
+ menu = pmenu;
+ cl = -1;
+ ct = new QTimer(this);
+ connect(ct, SIGNAL(timeout()), this, SLOT(done()));
+}
+
+/*
+ * Destructor is empty
+ */
+KBgEngine::~KBgEngine()
+{
+ // empty
+}
+
+/*
+ * Set the length of the commit timeout. Negative values disable the
+ * feature.
+ */
+void KBgEngine::setCommit(const double com)
+{
+ cl = int(1000*com);
+}
+
+// EOF
diff --git a/kbackgammon/engines/generic/kbgengine.h b/kbackgammon/engines/generic/kbgengine.h
new file mode 100644
index 00000000..ee672f40
--- /dev/null
+++ b/kbackgammon/engines/generic/kbgengine.h
@@ -0,0 +1,298 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+#ifndef __KBGENGINE_H
+#define __KBGENGINE_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <qobject.h>
+
+class QTimer;
+class QPopupMenu;
+
+class KDialogBase;
+
+class KBgStatus;
+
+/**
+ *
+ * Abstract class for a generic backgammon engine. Real engines have
+ * to inherit this and implement the interfaces.
+ *
+ * Engines can and will use the following global events described in
+ * the file eventsrc:
+ *
+ * "game over l"
+ * "game over w"
+ *
+ * "roll"
+ * "roll or double"
+ *
+ * "move"
+ * "invitation"
+ *
+ * @short Abstract base class for backgammon engines
+ * @author Jens Hoefkens <jens@hoefkens.com>
+ *
+ */
+class KBgEngine:public QObject
+{
+ Q_OBJECT public:
+
+ enum Command { Redo, Undo, Roll, Cube, Done, Load };
+
+ /**
+ * Constructor
+ */
+ KBgEngine (QWidget * parent = 0, QString * name = 0, QPopupMenu * pmenu = 0);
+
+ /**
+ * Destructor
+ */
+ virtual ~KBgEngine ();
+
+ /**
+ * Fills the engine-specific page into the notebook
+ */
+ virtual void getSetupPages (KDialogBase * nb) = 0;
+
+ /**
+ * Called after the user clicked ok in the setup dialog. Time
+ * to save settings.
+ */
+ virtual void setupOk () = 0;
+
+ /**
+ * The user cancelled the setup
+ */
+ virtual void setupCancel () = 0;
+
+ /**
+ * Set engine defaults
+ */
+ virtual void setupDefault () = 0;
+
+ /**
+ * Called when the windows are about to be hidden. Engines
+ * should hide all their child windows.
+ *
+ * The default implementation does nothing.
+ */
+ virtual void hideEvent ()
+ {
+ }
+
+ /**
+ * Called when the windows are about to be shown. Engines
+ * should show all visible child windows.
+ *
+ * The default implementation does nothing.
+ */
+ virtual void showEvent ()
+ {
+ }
+
+ /**
+ * Start the engine. This is called pretty much right after
+ * the constructor. While the constructor may not have any
+ * user interaction, it is possible to display dialogs in
+ * start.
+ *
+ * The default implementation does nothing.
+ */
+ virtual void start ()
+ {
+ }
+
+ /**
+ * Check with the engine if we can quit. This may require user
+ * interaction.
+ *
+ * The default implementation returns true.
+ */
+ virtual bool queryClose ()
+ {
+ return true;
+ }
+
+ /**
+ * About to be closed. Let the engine exit properly.
+ *
+ * The default implementation returns true.
+ */
+ virtual bool queryExit ()
+ {
+ return true;
+ }
+
+ /**
+ * Set the length of the commit timeout. Negative values
+ * disable the feature.
+ */
+ void setCommit (const double com = 2.5);
+
+public slots:
+ /**
+ * Read user settings from the config file
+ */
+ virtual void readConfig () = 0;
+
+ /**
+ * Save user settings to the config file
+ */
+ virtual void saveConfig () = 0;
+
+ /**
+ * Roll dice for the player w
+ */
+ virtual void rollDice (const int w) = 0;
+
+ /**
+ * Double the cube of player w
+ */
+ virtual void doubleCube (const int w) = 0;
+
+ /**
+ * A move has been made on the board - see the board class
+ * for the format of the string s
+ */
+ virtual void handleMove (QString * s) = 0;
+
+ /**
+ * Undo the last move
+ */
+ virtual void undo () = 0;
+
+ /**
+ * Redo the last move
+ */
+ virtual void redo () = 0;
+
+ /**
+ * Roll dice for whoevers turn it is
+ */
+ virtual void roll () = 0;
+
+ /**
+ * Double the cube for whoevers can double right now
+ */
+ virtual void cube () = 0;
+
+ /**
+ * Reload the board to the last known sane state
+ */
+ virtual void load () = 0;
+
+ /**
+ * Commit a move
+ */
+ virtual void done () = 0;
+
+ /**
+ * Process the string cmd
+ */
+ virtual void handleCommand (const QString & cmd) = 0;
+
+ /**
+ * Start a new game
+ */
+ virtual void newGame ()
+ {
+ }
+
+ /**
+ * Can we start a new game?
+ */
+ virtual bool haveNewGame ()
+ {
+ return false;
+ }
+
+signals:
+
+ /**
+ * The text identifies the current game status - could be put
+ * into the main window caption
+ */
+ void statText (const QString & msg);
+
+ /**
+ * Text that should be displayed in the ongoing message window
+ */
+ void infoText (const QString & msg);
+
+ /**
+ * Emit the most recent game state
+ */
+ void newState (const KBgStatus &);
+
+ /**
+ * Tell the board that we need the current state of the board.
+ */
+ void getState (KBgStatus *);
+
+ /**
+ * Starts/ends the edit mode of the board
+ */
+ void setEditMode (const bool f);
+
+ /**
+ * Toggle RO/RW flag of the board
+ */
+ void allowMoving (const bool fl);
+
+ /**
+ * Announce that we will accept/reject the command cmd from
+ * now on
+ */
+ void allowCommand (int cmd, bool f);
+
+ /**
+ * Tell the board to undo the last move
+ */
+ void undoMove ();
+
+ /**
+ * Tell the board to redo the last undone move
+ */
+ void redoMove ();
+
+protected:
+
+ /**
+ * Context menu for the board
+ */
+ QPopupMenu * menu;
+
+ /**
+ * Commit timer
+ */
+ QTimer *ct;
+ int cl;
+
+};
+
+#endif // __KBGENGINE_H
diff --git a/kbackgammon/engines/gnubg/Makefile.am b/kbackgammon/engines/gnubg/Makefile.am
new file mode 100644
index 00000000..7c0c9e1e
--- /dev/null
+++ b/kbackgammon/engines/gnubg/Makefile.am
@@ -0,0 +1,9 @@
+noinst_LTLIBRARIES = libkbggnubg.la
+
+libkbggnubg_la_SOURCES = kbggnubg.cpp
+
+INCLUDES= -I$(top_srcdir)/kbackgammon -I$(top_srcdir)/kbackgammon/engines \
+ $(all_includes)
+
+METASOURCES = AUTO
+
diff --git a/kbackgammon/engines/gnubg/kbggnubg.cpp b/kbackgammon/engines/gnubg/kbggnubg.cpp
new file mode 100644
index 00000000..eaaa4bdf
--- /dev/null
+++ b/kbackgammon/engines/gnubg/kbggnubg.cpp
@@ -0,0 +1,710 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+#include "kbggnubg.moc"
+#include "kbggnubg.h"
+
+#include <kapplication.h>
+#include <kmessagebox.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <qlayout.h>
+#include <kiconloader.h>
+#include <kstdaction.h>
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <kconfig.h>
+#include <iostream>
+#include <klocale.h>
+#include <kmainwindow.h>
+#include <klineeditdlg.h>
+#include <qregexp.h>
+#include <kregexp.h>
+#include <knotifyclient.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <signal.h>
+
+#include "kbgstatus.h"
+#include "kbgboard.h"
+#include "version.h"
+
+
+// == cube =====================================================================
+
+/*
+ * Double the cube for the player that can double - asks player
+ */
+void KBgEngineGNU::cube()
+{
+ handleCommand("double");
+}
+
+/*
+ * Double the cube for player w
+ */
+void KBgEngineGNU::doubleCube(const int w)
+{
+ dummy = w; // avoid compiler warning
+ cube();
+}
+
+
+
+
+
+void KBgEngineGNU::handleLine(const QString &l)
+{
+ if (l.isEmpty())
+ return;
+
+ QString line(l);
+
+ /*
+ * Start of a new game/match
+ */
+ if (line.contains(QRegExp("^gnubg rolls [1-6], .* rolls [1-6]\\."))) {
+ KRegExp e("^gnubg rolls ([1-6]), .* rolls ([1-6])\\.");
+ e.match(line.latin1());
+ if (int r = strcmp(e.group(1), e.group(2)))
+ turn = (r < 0) ? uRoll : tRoll;
+ }
+
+ /*
+ * Bug fixes for older versions of GNUBG - to be removed
+ */
+ if (line.contains(QRegExp("^.* cannot move\\..+$"))) {
+ KRegExp e("(^.* cannot move.)(.*$)");
+ e.match(line.latin1());
+ handleLine(e.group(1));
+ handleLine(e.group(2));
+ return;
+ }
+ if (line.contains(QRegExp("^Are you sure you want to start a new game, and discard the one in progress\\?"))) {
+ KRegExp e("(^Are you sure you want to start a new game, and discard the one in progress\\? )(.+$)");
+ e.match(line.latin1());
+ handleLine(e.group(1));
+ handleLine(e.group(2));
+ return;
+ }
+
+ /*
+ * Cube handling
+ */
+ if (line.contains(QRegExp("^gnubg accepts and immediately redoubles to [0-9]+\\.$"))) {
+
+ // redoubles mess up the game counter "turn"
+
+ //KBgStatus st(board);
+ //st.setCube(32, BOTH);
+ //emit newState(st);
+
+ }
+ if (line.contains(QRegExp("^gnubg doubles\\.$"))) {
+
+ // TODO: we need some generic class for this. the class
+ // can be shared between all engines
+
+#if 0
+ KBgStatus st(board);
+
+ int ret = KMessageBox::warningYesNoCancel
+ (0, i18n("gnubg doubles the cube to %1.").arg(2*st.cube(THEM)),
+ i18n("gnubg doubles"),
+ i18n("&Accept"), i18n("Re&double"), i18n("&Reject"), true);
+
+ switch (ret) {
+
+ case KMessageBox::Yes:
+ handleCommand("accept");
+ break;
+
+ case KMessageBox::No:
+ handleCommand("redouble");
+ break;
+
+ case KMessageBox::Cancel:
+ handleCommand("reject");
+ break;
+ }
+#endif
+ }
+
+ /*
+ * Ignore the following messages
+ */
+ if (line.contains(QRegExp("^TTY boards will be given in raw format"))) {
+ line = " ";
+ }
+
+ /*
+ * Board messages
+ */
+ if (line.contains(QRegExp("^board:"))) {
+
+ KBgStatus st(line);
+
+ /*
+ * Do preliminary analysis of board
+ */
+ if (st.doubled()) {
+ --turn;
+ return;
+ }
+ if (strcmp(board.latin1(),line.latin1()))
+ ++turn %= maxTurn;
+ board = line;
+
+ /*
+ * Act according to the current state in the move/roll cycle
+ */
+ switch (turn) {
+
+ case uRoll:
+
+ if (st.cube() > 0) {
+ emit infoText(i18n("Please roll or double."));
+ KNotifyClient::event("roll or double");
+ } else {
+ emit infoText(i18n("Please roll."));
+ KNotifyClient::event("roll");
+ }
+
+ emit allowCommand(Roll, true);
+ emit allowCommand(Cube, true);
+ break;
+
+ case uMove:
+ st.setDice(THEM, 0, 0);
+ st.setDice(THEM, 1, 0);
+ emit infoText(i18n("You roll %1 and %2.").arg(st.dice(US, 0)).arg(st.dice(US, 1)));
+ switch (st.moves()) {
+ case 0:
+ // get a message
+ break;
+ case 1:
+ emit infoText(i18n("Please move 1 piece."));
+ break;
+ default:
+ emit infoText(i18n("Please move %1 pieces.").arg(st.moves()));
+ break;
+ }
+ emit allowCommand(Roll, false);
+ break;
+
+ case tRoll:
+ break;
+
+ case tMove:
+ st.setDice(US, 0, 0);
+ st.setDice(US, 1, 0);
+ emit infoText(i18n("gnubg rolls %1 and %2.").arg(st.dice(THEM, 0)).arg(st.dice(THEM, 1)));
+ if (st.moves() == 0)
+ emit infoText(i18n("gnubg cannot move."));
+
+ break;
+
+ }
+
+ /*
+ * Bookkeeping
+ */
+ undoCounter = 0;
+ toMove = st.moves();
+ emit allowMoving(st.turn() == US);
+ emit newState(st);
+
+ emit statText(i18n("%1 vs. %2").arg(st.player(US)).arg(st.player(THEM)));
+
+ emit allowCommand(Load, true );
+ emit allowCommand(Undo, false);
+ emit allowCommand(Redo, false);
+ emit allowCommand(Done, false);
+ return;
+ }
+
+ /*
+ * Show the line...
+ */
+ line.replace(QRegExp(" "), "&nbsp;");
+ if (!line.isEmpty())
+ emit infoText(line);
+}
+
+
+/*
+ * Handle textual commands. All commands are passed to gnubg.
+ */
+void KBgEngineGNU::handleCommand(const QString& cmd)
+{
+ cmdList += cmd;
+ nextCommand();
+}
+
+
+
+// == start and init games =====================================================
+
+/*
+ * Start a new game.
+ */
+void KBgEngineGNU::newGame()
+{
+ /*
+ * If there is a game running we warn the user first
+ */
+ if (gameRunning && (KMessageBox::warningYesNo((QWidget *)parent(),
+ i18n("A game is currently in progress. "
+ "Starting a new one will terminate it."),
+ QString::null, i18n("Start New Game"), i18n("Continue Old Game"))
+ == KMessageBox::No))
+ return;
+
+ /*
+ * Start new game
+ */
+ handleCommand("new game");
+ if (gameRunning)
+ handleCommand("yes");
+
+ gameRunning = true;
+
+ emit infoText(i18n("Starting a new game."));
+}
+
+
+
+// == various slots & functions ================================================
+
+/*
+ * Quitting is fine at any time
+ */
+bool KBgEngineGNU::queryClose()
+{
+ return true;
+}
+
+/*
+ * Quitting is fine at any time
+ */
+bool KBgEngineGNU::queryExit()
+{
+ return true;
+}
+
+/*
+ * Load the last known sane state of the board
+ */
+void KBgEngineGNU::load()
+{
+ handleCommand("show board");
+}
+
+/*
+ * Store if cmd is allowed or not
+ */
+void KBgEngineGNU::setAllowed(int cmd, bool f)
+{
+ switch (cmd) {
+ case Roll:
+ rollingAllowed = f;
+ return;
+ case Undo:
+ undoPossible = f;
+ return;
+ case Cube:
+ doublePossible = f;
+ return;
+ case Done:
+ donePossible = f;
+ return;
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// == configuration handling ===================================================
+
+void KBgEngineGNU::setupOk()
+{
+ // nothing yet
+}
+
+void KBgEngineGNU::setupCancel()
+{
+ // nothing yet
+}
+
+void KBgEngineGNU::setupDefault()
+{
+ // nothing yet
+}
+
+void KBgEngineGNU::getSetupPages(KDialogBase *nb)
+{
+ /*
+ * Main Widget
+ */
+ QVBox *w = nb->addVBoxPage(i18n("GNU Engine"), i18n("Here you can configure the GNU backgammon engine"),
+ kapp->iconLoader()->loadIcon(PROG_NAME "_engine", KIcon::Desktop));
+}
+
+/*
+ * Restore settings
+ */
+void KBgEngineGNU::readConfig()
+{
+ KConfig* config = kapp->config();
+ config->setGroup("gnu engine");
+
+ // nothing yet
+}
+
+/*
+ * Save the engine specific settings
+ */
+void KBgEngineGNU::saveConfig()
+{
+ KConfig* config = kapp->config();
+ config->setGroup("gnu engine");
+
+ // nothing yet
+}
+
+
+
+// *****************************************************************************
+// *****************************************************************************
+// *****************************************************************************
+// *****************************************************************************
+// *****************************************************************************
+// *****************************************************************************
+
+
+
+
+// == constructor, destructor and other ========================================
+
+/*
+ * Constructor
+ */
+KBgEngineGNU::KBgEngineGNU(QWidget *parent, QString *name, QPopupMenu *pmenu)
+ : KBgEngine(parent, name, pmenu)
+{
+ // obsolete
+ nameUS = "US";
+ nameTHEM = "THEM";
+ random.setSeed(getpid()*time(NULL));
+
+ /*
+ * internal statue variables
+ */
+ rollingAllowed = undoPossible = gameRunning = donePossible = false;
+ connect(this, SIGNAL(allowCommand(int, bool)), this, SLOT(setAllowed(int, bool)));
+
+ /*
+ * Setup of menu
+ */
+ resAction = new KAction(i18n("&Restart GNU Backgammon"), 0, this, SLOT(startGNU()), this);
+ resAction->setEnabled(false); resAction->plug(menu);
+
+ /*
+ * Restore last stored settings
+ */
+ readConfig();
+}
+
+/*
+ * Destructor. Kill the child process and that's it.
+ */
+KBgEngineGNU::~KBgEngineGNU()
+{
+ gnubg.kill();
+}
+
+
+// == start, restart, termination of gnubg =====================================
+
+/*
+ * Start the GNU Backgammon process in the background and set up
+ * some communication links.
+ */
+void KBgEngineGNU::start()
+{
+ /*
+ * Will be started later
+ */
+ cmdTimer = new QTimer(this);
+ connect(cmdTimer, SIGNAL(timeout()), SLOT(nextCommand()) );
+
+ emit infoText(i18n("This is experimental code which currently requires a specially "
+ "patched version of GNU Backgammon.<br/><br/>"));
+
+ /*
+ * Initialize variables
+ */
+ partline = board = "";
+
+ /*
+ * Start the process - this requires that gnubg is in the PATH
+ */
+ gnubg << "gnubg" << "--tty";
+
+ connect(&gnubg, SIGNAL(processExited(KProcess *)), this, SLOT(gnubgExit(KProcess *)));
+ connect(&gnubg, SIGNAL(receivedStderr(KProcess *, char *, int)),
+ this, SLOT(receiveData(KProcess *, char *, int)));
+ connect(&gnubg, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ this, SLOT(receiveData(KProcess *, char *, int)));
+ connect(&gnubg, SIGNAL(wroteStdin(KProcess *)), this, SLOT(wroteStdin(KProcess *)));
+
+ startGNU();
+}
+
+/*
+ * Actually start the background process.
+ */
+void KBgEngineGNU::startGNU()
+{
+
+ resAction->setEnabled(false);
+
+ if (!gnubg.start(KProcess::NotifyOnExit, KProcess::All))
+ KMessageBox::information((QWidget *)parent(),
+ i18n("Could not start the GNU Backgammon process.\n"
+ "Make sure the program is in your PATH and is "
+ "called \"gnubg\".\n"
+ "Make sure that your copy is at least version 0.10"));
+
+ /*
+ * Set required gnubg options
+ */
+ handleCommand("set output rawboard on");
+}
+
+/*
+ * The gnubg process has died. Stop all user activity and allow a restart.
+ */
+void KBgEngineGNU::gnubgExit(KProcess *proc)
+{
+ ct->stop();
+
+ cmdTimer->stop();
+
+ emit allowCommand(Undo, false);
+ emit allowCommand(Roll, false);
+ emit allowCommand(Done, false);
+ emit allowCommand(Cube, false);
+ emit allowCommand(Load, false);
+
+ emit allowMoving(false);
+
+ emit infoText(QString("<br/><font color=\"red\">") + i18n("The GNU Backgammon process (%1) has exited. ")
+ .arg(proc->pid()) + "</font><br/>");
+
+ resAction->setEnabled(true);
+}
+
+
+// == communication callbacks with GNU bg ======================================
+
+/*
+ * Last command has been sent. Try to send pending ones.
+ */
+void KBgEngineGNU::wroteStdin(KProcess *proc)
+{
+ if (!proc->isRunning())
+ return;
+ nextCommand();
+}
+
+/*
+ * Try to send the next command from the command list to gnubg.
+ * If it fails, make sure we call ourselves again.
+ */
+void KBgEngineGNU::nextCommand()
+{
+ if (!gnubg.isRunning())
+ return;
+
+ for (QStringList::Iterator it = cmdList.begin(); it != cmdList.end(); ++it) {
+ QString s = (*it) + "\n";
+ if (!gnubg.writeStdin(s.latin1(), strlen(s.latin1()))) {
+ cmdTimer->start(250, true);
+ cmdList.remove(QString::null);
+ return;
+ }
+ (*it) = QString::null;
+ }
+ cmdList.remove(QString::null);
+ cmdTimer->stop();
+}
+
+/*
+ * Get data from GNU Backgammon and process them. Note that we may have
+ * to buffer the last line and wait for the closing newline...
+ */
+void KBgEngineGNU::receiveData(KProcess *proc, char *buffer, int buflen)
+{
+ if (!proc->isRunning())
+ return;
+
+ char *buf = new char[buflen+1];
+
+ memcpy(buf, buffer, buflen);
+ buf[buflen] = '\0';
+
+ QStringList l(QStringList::split('\n', buf, true));
+
+ /*
+ * Restore partial lines from the previous time
+ */
+ l.first() = partline + l.first();
+ partline = "";
+ if (buf[buflen-1] != '\n') {
+ partline = l.last();
+ l.remove(partline);
+ }
+
+ delete[] buf;
+
+ /*
+ * Handle the information from gnubg
+ */
+ for (QStringList::Iterator it = l.begin(); it != l.end(); ++it)
+ handleLine(*it);
+}
+
+
+// == moving ===================================================================
+
+/*
+ * Finish the last move - called by the timer and directly by the user
+ */
+void KBgEngineGNU::done()
+{
+ ct->stop();
+
+ emit allowMoving(false);
+
+ emit allowCommand(Done, false);
+ emit allowCommand(Undo, false);
+ emit allowCommand(Redo, false);
+
+ // Transform the string to FIBS format
+ lastmove.replace(0, 2, "move ");
+ lastmove.replace(QRegExp("\\+"), " ");
+ lastmove.replace(QRegExp("\\-"), " ");
+
+ // sent it to the server
+ handleCommand(lastmove);
+}
+
+/*
+ * Undo the last move
+ */
+void KBgEngineGNU::undo()
+{
+ ct->stop();
+
+ redoPossible = true;
+ ++undoCounter;
+
+ emit allowMoving(true);
+
+ emit allowCommand(Done, false);
+ emit allowCommand(Redo, true);
+
+ emit undoMove();
+}
+
+/*
+ * Redo the last move
+ */
+void KBgEngineGNU::redo()
+{
+ --undoCounter;
+ emit redoMove();
+}
+
+/*
+ * Take the move string and make the changes on the working copy
+ * of the state.
+ */
+void KBgEngineGNU::handleMove(QString *s)
+{
+ lastmove = *s;
+
+ int index = 0;
+ QString t = s->mid(index, s->find(' ', index));
+ index += 1 + t.length();
+ int moves = t.toInt();
+
+ /*
+ * Allow undo and possibly start the commit timer
+ */
+ redoPossible &= ((moves < toMove) && (undoCounter > 0));
+
+ emit allowCommand(Undo, moves > 0);
+ emit allowCommand(Redo, redoPossible);
+ emit allowCommand(Done, moves == toMove);
+
+ if (moves == toMove && cl >= 0) {
+ emit allowMoving(false);
+ ct->start(cl, true);
+ }
+}
+
+
+// == dice & rolling ===========================================================
+
+/*
+ * Roll random dice for the player whose turn it is. We can ignore the
+ * value of w, since we have the turn value.
+ */
+void KBgEngineGNU::roll()
+{
+ if (turn == uRoll)
+ handleCommand("roll");
+}
+void KBgEngineGNU::rollDice(const int w)
+{
+ roll();
+}
+
+
+
+// EOF
diff --git a/kbackgammon/engines/gnubg/kbggnubg.h b/kbackgammon/engines/gnubg/kbggnubg.h
new file mode 100644
index 00000000..3240b8b1
--- /dev/null
+++ b/kbackgammon/engines/gnubg/kbggnubg.h
@@ -0,0 +1,223 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+#ifndef __KBGGNU_H
+#define __KBGGNU_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <generic/kbgengine.h>
+
+#include <qtimer.h>
+#include <qspinbox.h>
+#include <kaction.h>
+#include <krandomsequence.h>
+#include <kprocess.h>
+#include <qstringlist.h>
+
+/**
+ *
+ *
+ */
+class KBgEngineGNU : public KBgEngine
+{
+ Q_OBJECT
+
+public:
+
+ /*
+ * Constructor and destructor
+ */
+ KBgEngineGNU(QWidget *parent = 0, QString *name = 0, QPopupMenu *pmenu = 0);
+ virtual ~KBgEngineGNU();
+
+ /**
+ * Fills the engine-specific page into the notebook
+ */
+ virtual void getSetupPages(KDialogBase *nb);
+
+ virtual void setupOk();
+ virtual void setupDefault();
+ virtual void setupCancel();
+
+ /*
+ * Check with the engine if we can quit. This may require user
+ * interaction.
+ */
+ virtual bool queryClose();
+
+ /**
+ * About to be closed. Let the engine exit properly.
+ */
+ virtual bool queryExit();
+
+ virtual void start();
+
+public slots:
+
+ /**
+ * Read user settings from the config file
+ */
+ virtual void readConfig();
+
+ /**
+ * Save user settings to the config file
+ */
+ virtual void saveConfig();
+
+ /**
+ * Double the cube of player w
+ */
+ virtual void doubleCube(const int w);
+
+ /**
+ * A move has been made on the board - see the board class
+ * for the format of the string s
+ */
+ virtual void handleMove(QString *s);
+
+ /**
+ * Undo the last move
+ */
+ virtual void undo();
+
+ /**
+ * Redo the last move
+ */
+ virtual void redo();
+
+ /**
+ * Roll dice for whoevers turn it is
+ */
+ virtual void roll();
+
+ /**
+ * Double the cube for whoevers can double right now
+ */
+ virtual void cube();
+
+ /**
+ * Reload the board to the last known sane state
+ */
+ virtual void load();
+
+ /**
+ * Commit a move
+ */
+ virtual void done();
+
+ /*
+ * Roll dice for the player w
+ */
+ virtual void rollDice(const int w);
+
+ /**
+ * Process the string cmd
+ */
+ virtual void handleCommand(const QString& cmd);
+
+ /**
+ * Start a new game.
+ */
+ virtual void newGame();
+ virtual bool haveNewGame() {return true;}
+
+protected slots:
+
+ /**
+ * Store if cmd is allowed or not
+ */
+ void setAllowed(int cmd, bool f);
+
+ void startGNU();
+
+private:
+
+ /**
+ * Use the standard method of obtaining random numbers
+ */
+ KRandomSequence random;
+
+ /**
+ * Player's names
+ */
+ QString nameUS, nameTHEM;
+
+ /**
+ * Who did the last roll
+ */
+ int lastRoll;
+
+ /**
+ * How many checkers to move
+ */
+ int toMove;
+
+ /**
+ * Various flags, representing the current status of the game
+ */
+ bool rollingAllowed, undoPossible, donePossible;
+ bool gameRunning, redoPossible, doublePossible;
+
+ /**
+ * Count the number of available undos
+ */
+ int dummy, undoCounter;
+
+private:
+
+ enum Turn {uRoll, uMove, tRoll, tMove, maxTurn};
+
+ KProcess gnubg;
+
+ QStringList cmdList;
+
+ QTimer *cmdTimer;
+
+ QString partline;
+
+ QString board;
+
+ QString lastmove;
+
+ int turn;
+
+ KAction *resAction;
+
+protected slots:
+
+ void wroteStdin(KProcess *);
+
+ void receiveData(KProcess *, char *buffer, int buflen);
+
+ void handleLine(const QString &l);
+
+ void gnubgExit(KProcess *proc);
+
+ void nextCommand();
+
+};
+
+#endif // __KBGGNU_H
diff --git a/kbackgammon/engines/nextgen/Makefile.am b/kbackgammon/engines/nextgen/Makefile.am
new file mode 100644
index 00000000..ed58d2f4
--- /dev/null
+++ b/kbackgammon/engines/nextgen/Makefile.am
@@ -0,0 +1,9 @@
+noinst_LTLIBRARIES = libkbgnextgen.la
+
+libkbgnextgen_la_SOURCES = kbgng.cpp kbgplayer.cpp kbggame.cpp
+
+INCLUDES= -I$(top_srcdir)/kbackgammon -I$(top_srcdir)/kbackgammon/engines \
+ -I$(top_srcdir)/libkdegames/kgame $(all_includes)
+
+METASOURCES = AUTO
+
diff --git a/kbackgammon/engines/nextgen/kbggame.cpp b/kbackgammon/engines/nextgen/kbggame.cpp
new file mode 100644
index 00000000..6ee709e1
--- /dev/null
+++ b/kbackgammon/engines/nextgen/kbggame.cpp
@@ -0,0 +1,47 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+#include "kbggame.moc"
+#include "kbggame.h"
+
+#include <kplayer.h>
+
+#include <iostream>
+
+/*
+ * Constructor
+ */
+KBgGame::KBgGame(int cookie, QObject *parent)
+ : KGame(cookie, parent)
+{
+ // do nothing...
+}
+
+bool KBgGame::playerInput(QDataStream &msg,KPlayer *player)
+{
+ Q_INT32 move;
+ msg >> move;
+ cerr << " Player " << player->id() << " moved to " << move << endl;
+ return true;
+}
+
diff --git a/kbackgammon/engines/nextgen/kbggame.h b/kbackgammon/engines/nextgen/kbggame.h
new file mode 100644
index 00000000..fea5f516
--- /dev/null
+++ b/kbackgammon/engines/nextgen/kbggame.h
@@ -0,0 +1,57 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+#ifndef __KBGGAME_H
+#define __KBGGAME_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <kgame.h>
+#include <kdemacros.h>
+class QObject;
+class KPlayer;
+
+/**
+ *
+ *
+ */
+class KDE_EXPORT KBgGame : public KGame
+{
+ Q_OBJECT
+
+public:
+
+ enum MsgID {Text, Cmd, MaxMsg};
+
+ KBgGame(int cookie = 42, QObject *parent = 0);
+
+protected:
+
+ virtual bool playerInput(QDataStream &msg,KPlayer *player);
+
+};
+
+#endif // __KBGGAME_H
+
diff --git a/kbackgammon/engines/nextgen/kbgng.cpp b/kbackgammon/engines/nextgen/kbgng.cpp
new file mode 100644
index 00000000..6518147c
--- /dev/null
+++ b/kbackgammon/engines/nextgen/kbgng.cpp
@@ -0,0 +1,622 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+#include "kbgng.moc"
+#include "kbgng.h"
+
+#include <kapplication.h>
+#include <kmessagebox.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <qlayout.h>
+#include <kiconloader.h>
+#include <kstdaction.h>
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kmainwindow.h>
+#include <klineeditdlg.h>
+#include <krandomsequence.h>
+#include <qstringlist.h>
+#include <qcstring.h>
+#include <qtextstream.h>
+
+#include <version.h>
+
+#include <iostream>
+
+
+/*
+ * Constructor
+ */
+KBgEngineNg::KBgEngineNg(QWidget *parent, QString *name, QPopupMenu *pmenu)
+ : KBgEngine(parent, name, pmenu)
+{
+ // get a new game
+ initGame();
+
+ // create actions and menus
+ QString label[MaxTypes];
+
+ label[Local ] = i18n("Local Games");
+ label[NetServer] = i18n("Offer Network Games");
+ label[NetClient] = i18n("Join Network Games");
+
+ QStringList list;
+ for (int i = 0; i < MaxTypes; i++)
+ list.append(label[i]);
+
+ _gameSelect = new KSelectAction(i18n("&Types"), 0, this, SLOT(setGame()), this);
+ _gameSelect->setItems(list);
+ _gameSelect->plug(menu);
+
+ menu->insertSeparator();
+
+ _connectAction = new KAction(i18n("&Names..."), 0, this, SLOT(changeName()), this);
+ _connectAction->plug(menu);
+
+ // Restore last settings
+ readConfig();
+
+ // initialize to local games
+ _player[0] = _player[1] = 0;
+ _currGame = None;
+ _gameSelect->setCurrentItem(Local);
+ setGame();
+}
+
+
+/*
+ * Switch the local game type. This is called by the menu...
+ *
+ * TODO: lots of work and testing needed...
+ */
+void KBgEngineNg::setGame()
+{
+ // shutdown old game
+ switch (_currGame) {
+
+ case Local:
+ // nothing to do...
+ break;
+
+ case NetServer:
+ _game->stopServerConnection();
+ break;
+
+ case NetClient:
+ _game->disconnect();
+ break;
+
+ default:
+ // ignore
+ break;
+ }
+
+ // reset the game and delete the players
+ delete _game;
+ initGame();
+
+ emit infoText("<br/>");
+
+ // initialize a new game
+ bool ret = false;
+ QString label, port_s, host_s;
+ Q_UINT16 port;
+
+ switch (_currGame = _gameSelect->currentItem()) {
+
+ case Local:
+
+ _game->addPlayer(createPlayer(0, _name[0]));
+ _game->addPlayer(createPlayer(1, _name[1]));
+ break;
+
+ case NetServer:
+ label = i18n("Type the port number on which you want to listen to "
+ "connections.\nThe number should be between 1024 and "
+ "65535.");
+ port_s.setNum(_port);
+ do {
+ port_s = KLineEditDlg::getText(label, port_s, &ret, (QWidget *)parent());
+ if (!ret)
+ return;
+ port = port_s.toUShort(&ret);
+ } while (port_s.isEmpty() && !ret);
+
+ if (_game->offerConnections(port))
+ emit infoText(i18n("Now waiting for incoming connections on port %1.").
+ arg(_port = port));
+ else
+ emit infoText(i18n("Failed to offer connections on port %1.").arg(port));
+
+ _game->addPlayer(createPlayer(0, _name[0]));
+ break;
+
+ case NetClient:
+ label = i18n("Type the name of the server you want to connect to:");
+ host_s = _host;
+ do {
+ host_s = KLineEditDlg::getText(label, host_s, &ret, (QWidget *)parent());
+ if (!ret)
+ return;
+ } while (host_s.isEmpty());
+
+ label = i18n("Type the port number on %1 you want to connect to.\nThe "
+ "number should be between 1024 and 65535.").arg(host_s);
+ port_s.setNum(_port);
+ do {
+ port_s = KLineEditDlg::getText(label, port_s, &ret, (QWidget *)parent());
+ if (!ret)
+ return;
+ port = port_s.toUShort(&ret);
+ } while (port_s.isEmpty() && !ret);
+
+ /*
+ * Hi Martin: another thing you night want to try is to move this to the
+ * place marked by <HERE> (about 10 lines further down. If you do that, the
+ * players are created properly on the server, but a total of three players
+ * is created on the client.
+ */
+ _game->addPlayer(createPlayer(0, _name[0]));
+
+ if (_game->connectToServer(host_s, port))
+ emit infoText(i18n("Now connected to %1:%2.").arg(_host = host_s).
+ arg(_port = port));
+ else
+ emit infoText(i18n("Failed to connect to %1:%2.").arg(_host = host_s).
+ arg(_port = port));
+
+ // <HERE>
+
+ break;
+
+ default:
+ kdDebug(true, PROG_COOKIE) << "setGame parameter invalid: "
+ << _currGame << endl;
+ _currGame = None;
+ return;
+ }
+
+ // we are still having problems with player creation...
+
+ // FIXME - which status _game->setGameStatus(KGame::End);
+}
+
+void KBgEngineNg::slotPlayerJoinedGame(KPlayer *p)
+{
+ emit infoText(i18n("Player %1 (%2) has joined the game.").arg(p->name()).arg(p->id()));
+ cerr << i18n("Player %1 (%2) has joined the game.").arg(p->name()).arg(p->id()).latin1() << endl;
+}
+
+void KBgEngineNg::slotCreatePlayer(KPlayer *&p, int rtti, int io, bool v, KGame *g)
+{
+ Q_UNUSED(rtti)
+ Q_UNUSED(g)
+ Q_UNUSED(io)
+ emit infoText(i18n("creating player. virtual=%1").arg(v));
+ p = createPlayer(1);
+}
+
+void KBgEngineNg::slotClientConnected(Q_UINT32)
+{
+ cerr << "client has joint the game..." << endl;
+}
+
+void KBgEngineNg::slotClientDisconnected(Q_UINT32, bool)
+{
+ cerr << "KBgEngineNg::slotClientDisconnected" << endl;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// == start and init games =====================================================
+
+/*
+ * Start a new game. ...
+ */
+void KBgEngineNg::newGame()
+{
+ // TODO
+ cerr << "games are not yet working..." << endl;
+}
+
+/*
+ * Finish the last move - called by the timer and directly by the used
+ */
+void KBgEngineNg::done()
+{
+ // empty
+}
+
+/*
+ * Undo the last move
+ */
+void KBgEngineNg::undo()
+{
+ // TODO
+}
+
+/*
+ * Redo the last move
+ */
+void KBgEngineNg::redo()
+{
+ // TODO
+}
+
+/*
+ * Take the move string and make the changes on the working copy
+ * of the state.
+ */
+void KBgEngineNg::handleMove(QString *s)
+{
+ Q_UNUSED(s)
+ // TODO
+}
+
+/*
+ * Roll random dice for the player whose turn it is
+ */
+void KBgEngineNg::roll()
+{
+ // empty
+}
+
+/*
+ * If possible, roll random dice for player w
+ */
+void KBgEngineNg::rollDice(const int w)
+{
+ Q_UNUSED(w)
+ // empty
+}
+
+/*
+ * Double the cube for the player that can double - asks player
+ */
+void KBgEngineNg::cube()
+{
+ // TODO
+}
+
+/*
+ * Double the cube for player w
+ */
+void KBgEngineNg::doubleCube(const int)
+{
+ cube();
+}
+
+/*
+ * Put the engine specific details in the setup dialog
+ */
+void KBgEngineNg::getSetupPages(KDialogBase *)
+{
+ // FIXME: do nothing...
+}
+
+/*
+ * Called when the setup dialog is positively closed
+ */
+void KBgEngineNg::setupOk()
+{
+ // FIXME: do nothing...
+}
+void KBgEngineNg::setupDefault()
+{
+ // FIXME: do nothing...
+}
+void KBgEngineNg::setupCancel()
+{
+ // FIXME: do nothing...
+}
+
+
+// == various slots & functions ================================================
+
+/*
+ * Check with the user if we should really quit in the middle of a
+ * game.
+ */
+bool KBgEngineNg::queryClose()
+{
+ return true;
+}
+
+/*
+ * Quitting is fine at any time
+ */
+bool KBgEngineNg::queryExit()
+{
+ return true;
+}
+
+/*
+ * Load the last known sane state of the board
+ */
+void KBgEngineNg::load()
+{
+ // TODO
+}
+
+/*
+ * Store if cmd is allowed or not
+ */
+void KBgEngineNg::setAllowed(int cmd, bool f)
+{
+ switch (cmd) {
+ case Roll:
+ rollingAllowed = f;
+ return;
+ case Undo:
+ undoPossible = f;
+ return;
+ case Cube:
+ doublePossible = f;
+ return;
+ case Done:
+ donePossible = f;
+ return;
+ }
+}
+
+
+// ********************************************************************************
+// ********************************************************************************
+
+// DONE
+
+// ********************************************************************************
+// ********************************************************************************
+
+
+/*
+ * Destructor.
+ */
+KBgEngineNg::~KBgEngineNg()
+{
+ saveConfig();
+ delete _game;
+}
+
+/*
+ * Restore settings
+ */
+void KBgEngineNg::readConfig()
+{
+ KConfig* config = kapp->config();
+ config->setGroup("next generation engine");
+
+ _port = config->readNumEntry("port", PROG_COOKIE);
+ _host = config->readEntry("host", "localhost");
+
+ _name[0] = config->readEntry("name_0", i18n("one"));
+ _name[1] = config->readEntry("name_1", i18n("two"));
+}
+
+/*
+ * Save the engine specific settings
+ */
+void KBgEngineNg::saveConfig()
+{
+ KConfig* config = kapp->config();
+ config->setGroup("next generation engine");
+
+ config->writeEntry("port", _port);
+ config->writeEntry("host", _host);
+
+ config->writeEntry("name_0", _name[0]);
+ config->writeEntry("name_1", _name[1]);
+}
+
+/*
+ * Read the users input from the command line and send it to all
+ * players. Although the message gets the Cmd ID, it is currently
+ * handled as a regular text message.
+ */
+void KBgEngineNg::handleCommand(const QString& text)
+{
+ QByteArray msg;
+ QTextStream ts(msg, IO_WriteOnly);
+ ts << text;
+ if (!_game->sendMessage(msg, KBgGame::Cmd))
+ kdDebug(true, PROG_COOKIE) << "couldn't send message: "
+ << text.latin1() << endl;
+}
+
+/*
+ * Return a random integer between 1 and 6. Use the KGame random
+ * number generator.
+ */
+int KBgEngineNg::getRandom()
+{
+ return 1+_game->random()->getLong(6);
+}
+
+/*
+ * A player propert has changed - check if we care
+ */
+void KBgEngineNg::slotPropertyChanged(KGamePropertyBase *p, KPlayer *me)
+{
+ int player = (me->id() == _player[1]->id());
+
+ switch (p->id()) {
+
+ case KGamePropertyBase::IdName:
+ emit infoText(i18n("Player %1 has changed the name to %2.")
+ .arg(_name[player]).arg(me->name()));
+ _name[player] = me->name();
+ break;
+
+ default:
+ kdDebug(true, PROG_COOKIE) << "KBgPlayer (" << me << ") property change ("
+ << p->id() << ") ignored" << endl;
+ break;
+ }
+}
+
+/*
+ * A game property has changed
+ */
+void KBgEngineNg::slotPropertyChanged(KGamePropertyBase *p, KGame *me)
+{
+ Q_UNUSED(me)
+ switch (p->id()) {
+
+ default:
+ kdDebug(true, PROG_COOKIE) << "Change in GameProperty " << p->id()
+ << " has been ignored." << endl;
+ break;
+ }
+}
+
+/*
+ * Change the names of all local players
+ */
+void KBgEngineNg::changeName()
+{
+ bool ok = false;
+ QString name;
+
+ for (int i = 0; i < 2; i++) {
+ name = QString::null;
+ while (!_player[i]->isVirtual() && name.isEmpty()) {
+ if (i == 0)
+ name = KLineEditDlg::getText(i18n("Type the name of the first player:"),
+ _name[i], &ok, (QWidget *)parent());
+ else
+ name = KLineEditDlg::getText(i18n("Type the name of the second player:"),
+ _name[i], &ok, (QWidget *)parent());
+ if (!ok) return;
+ _player[i]->setName(name);
+ }
+ }
+}
+
+/*
+ * Receive data sent via KBgGame::sendMessage(...)
+ */
+void KBgEngineNg::slotNetworkData(int msgid, const QByteArray &msg, Q_UINT32 r, Q_UINT32 s)
+{
+ Q_UNUSED(r);
+ Q_UNUSED(s);
+ switch (msgid) {
+
+ case KBgGame::Cmd:
+ emit infoText(msg);
+ emit infoText(i18n("Players are %1 and %2").arg(_player[0]->name())
+ .arg(_player[1]->name()));
+ break;
+
+ default:
+ kdDebug(true, PROG_COOKIE) << "Ignored message ID: " << msgid << endl;
+ break;
+ }
+}
+
+/*
+ * Create the i-th player
+ */
+KBgPlayer * KBgEngineNg::createPlayer(int i, QString name)
+{
+ KBgPlayer *p = new KBgPlayer();
+
+ if (!name.isNull())
+ p->setName(name);
+
+ p->findProperty(KGamePropertyBase::IdName)->setEmittingSignal(true);
+
+ connect(p, SIGNAL(signalPropertyChanged(KGamePropertyBase *, KPlayer *)),
+ this, SLOT(slotPropertyChanged(KGamePropertyBase *, KPlayer *)));
+
+ return (_player[i] = p);
+}
+
+/*
+ * Create and connect the game object
+ */
+void KBgEngineNg::initGame()
+{
+ _game = new KBgGame(PROG_COOKIE);
+ _game->random()->setSeed(getpid()*time(NULL));
+
+ connect(_game, SIGNAL(signalPlayerJoinedGame(KPlayer *)),
+ this, SLOT(slotPlayerJoinedGame(KPlayer *)));
+ connect(_game, SIGNAL(signalCreatePlayer(KPlayer *&, int, int, bool, KGame *)),
+ this, SLOT(slotCreatePlayer(KPlayer *&, int, int, bool, KGame *)));
+
+ connect(_game, SIGNAL(signalClientConnected(Q_UINT32)),
+ this, SLOT(slotClientConnected(Q_UINT32)));
+ connect(_game, SIGNAL(signalClientDisconnected(Q_UINT32, bool)),
+ this, SLOT(slotClientDisconnected(Q_UINT32, bool)));
+
+ connect(_game, SIGNAL(signalPropertyChanged(KGamePropertyBase *, KGame *)),
+ this, SLOT(slotPropertyChanged(KGamePropertyBase *, KGame *)));
+ connect(_game, SIGNAL(signalNetworkData(int,const QByteArray &, Q_UINT32, Q_UINT32)),
+ this, SLOT(slotNetworkData(int,const QByteArray &, Q_UINT32, Q_UINT32)));
+}
+
+// EOF
diff --git a/kbackgammon/engines/nextgen/kbgng.h b/kbackgammon/engines/nextgen/kbgng.h
new file mode 100644
index 00000000..149f3bf6
--- /dev/null
+++ b/kbackgammon/engines/nextgen/kbgng.h
@@ -0,0 +1,263 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+#ifndef __KBGNG_H
+#define __KBGNG_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+#include <qtimer.h>
+#include <qspinbox.h>
+#include <kaction.h>
+#include <qdatastream.h>
+#include <kgameproperty.h>
+
+#include <generic/kbgengine.h>
+
+#include "kbgboard.h"
+#include "kbgstatus.h"
+#include "kbgplayer.h"
+#include "kbggame.h"
+
+
+/**
+ *
+ * The interface of the next generation backgammon engine.
+ *
+ */
+class KBgEngineNg : public KBgEngine
+{
+ Q_OBJECT
+
+public:
+
+ /*
+ * Constructor and destructor
+ */
+ KBgEngineNg( QWidget *parent = 0, QString *name = 0, QPopupMenu *pmenu = 0);
+ virtual ~KBgEngineNg();
+
+ /**
+ * Fills the engine-specific page into the notebook
+ */
+ virtual void getSetupPages(KDialogBase *nb);
+
+ virtual void setupOk();
+ virtual void setupDefault();
+ virtual void setupCancel();
+
+ /*
+ * Check with the engine if we can quit. This may require user
+ * interaction.
+ */
+ virtual bool queryClose();
+
+ /**
+ * About to be closed. Let the engine exit properly.
+ */
+ virtual bool queryExit();
+
+
+public slots:
+
+ /**
+ * Read user settings from the config file
+ */
+ virtual void readConfig();
+
+ /**
+ * Save user settings to the config file
+ */
+ virtual void saveConfig();
+
+ /**
+ * Roll dice for the player w
+ */
+ virtual void rollDice(const int w);
+
+ /**
+ * Double the cube of player w
+ */
+ virtual void doubleCube(const int w);
+
+ /**
+ * A move has been made on the board - see the board class
+ * for the format of the string s
+ */
+ virtual void handleMove(QString *s);
+
+ /**
+ * Undo the last move
+ */
+ virtual void undo();
+
+ /**
+ * Redo the last move
+ */
+ virtual void redo();
+
+ /**
+ * Roll dice for whoevers turn it is
+ */
+ virtual void roll();
+
+ /**
+ * Double the cube for whoevers can double right now
+ */
+ virtual void cube();
+
+ /**
+ * Reload the board to the last known sane state
+ */
+ virtual void load();
+
+ /**
+ * Commit a move
+ */
+ virtual void done();
+
+ /**
+ * Process the string text
+ */
+ virtual void handleCommand(const QString& text);
+
+ /**
+ * Start a new game.
+ */
+ virtual void newGame();
+ virtual bool haveNewGame() {return true;}
+
+
+ void slotPlayerJoinedGame(KPlayer *p);
+ void slotNetworkData(int msgid, const QByteArray &msg, Q_UINT32 receiver, Q_UINT32 sender);
+ void slotCreatePlayer(KPlayer *&, int, int, bool, KGame *);
+
+ void slotClientDisconnected(Q_UINT32, bool);
+ void slotClientConnected(Q_UINT32);
+
+ void slotPropertyChanged(KGamePropertyBase *p, KGame *me);
+ void slotPropertyChanged(KGamePropertyBase *p, KPlayer *me);
+
+protected slots:
+
+ void initGame();
+
+ void setGame();
+
+ void changeName();
+
+protected:
+
+ void setAllowed(int cmd, bool f);
+
+private:
+
+
+ /**
+ * Who did the last roll
+ */
+ int lastRoll;
+
+ /**
+ * How many checkers to move
+ */
+ int toMove;
+
+ /**
+ * Various flags, representing the current status of the game
+ */
+ bool rollingAllowed, undoPossible, donePossible;
+ bool gameRunning, redoPossible, doublePossible;
+
+ /**
+ * Count the number of available undos
+ */
+ int dummy, undoCounter;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ enum GameTypes {None = -1, Local, NetServer, NetClient, MaxTypes};
+ KSelectAction * _gameSelect;
+ KAction* _connectAction;
+ KAction* _nameAction;
+ int _currGame;
+ int _nLocalPlayers;
+
+ int _nplayers;
+
+ QString _host;
+ Q_UINT16 _port;
+
+ // ************************************************************
+ // ************************************************************
+
+ // DONE
+
+ // ************************************************************
+ // ************************************************************
+
+
+protected:
+
+ /**
+ * Return a random integer between 1 and 6. The random numer
+ * is based on the @ref KRandomSequence of @ref KGame. Thus,
+ * the numbers should be synchronized across the network.
+ */
+ int getRandom();
+
+private:
+
+ /**
+ * Create the i-th player. Legal values for i are 0 and 1. The
+ * name of the player is taken from @ref _name and the parent of
+ * the player is @ref _player. That means that the players are
+ * automatically deleted.
+ */
+ KBgPlayer * createPlayer(int i, QString name = QString::null);
+
+private:
+
+ KBgGame* _game;
+
+ QString _name[2];
+
+ KBgPlayer* _player[2];
+
+};
+
+#endif // __KBGNG_H
diff --git a/kbackgammon/engines/nextgen/kbgplayer.cpp b/kbackgammon/engines/nextgen/kbgplayer.cpp
new file mode 100644
index 00000000..f0b3a7ed
--- /dev/null
+++ b/kbackgammon/engines/nextgen/kbgplayer.cpp
@@ -0,0 +1,62 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+#include "kbgplayer.moc"
+#include "kbgplayer.h"
+
+#include <kgame.h>
+
+#include <iostream>
+
+
+/*
+ * Constructors
+ */
+KBgPlayer::KBgPlayer()
+ : KPlayer()
+{
+ // do nothing...
+}
+KBgPlayer::KBgPlayer(KGame *game)
+ : KPlayer(game)
+{
+ // do nothing...
+}
+
+int KBgPlayer::rtti() const
+{
+ return 10500;
+}
+
+bool KBgPlayer::load(QDataStream &stream)
+{
+ KPlayer::load(stream);
+ cerr << "-------- KBgPlayer::load" << endl;
+ return false;
+}
+bool KBgPlayer::save(QDataStream &stream)
+{
+ KPlayer::save(stream);
+ cerr << "-------- KBgPlayer::save" << endl;
+ return false;
+}
diff --git a/kbackgammon/engines/nextgen/kbgplayer.h b/kbackgammon/engines/nextgen/kbgplayer.h
new file mode 100644
index 00000000..7c11d83c
--- /dev/null
+++ b/kbackgammon/engines/nextgen/kbgplayer.h
@@ -0,0 +1,58 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+#ifndef __KBGPLAYER_H
+#define __KBGPLAYER_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <kplayer.h>
+#include <qdatastream.h>
+
+class KGame;
+
+
+/**
+ *
+ *
+ */
+class KBgPlayer : public KPlayer
+{
+ Q_OBJECT
+
+public:
+
+ KBgPlayer();
+ KBgPlayer(KGame* game);
+
+ virtual int rtti() const;
+
+ virtual bool load(QDataStream &stream);
+ virtual bool save(QDataStream &stream);
+
+};
+
+#endif // __KBGPLAYER_H
+
diff --git a/kbackgammon/engines/offline/Makefile.am b/kbackgammon/engines/offline/Makefile.am
new file mode 100644
index 00000000..82d7a681
--- /dev/null
+++ b/kbackgammon/engines/offline/Makefile.am
@@ -0,0 +1,9 @@
+noinst_LTLIBRARIES = libkbgoffline.la
+
+libkbgoffline_la_SOURCES = kbgoffline.cpp
+
+INCLUDES= -I$(top_srcdir)/kbackgammon -I$(top_srcdir)/kbackgammon/engines \
+ $(all_includes)
+
+METASOURCES = AUTO
+
diff --git a/kbackgammon/engines/offline/kbgoffline.cpp b/kbackgammon/engines/offline/kbgoffline.cpp
new file mode 100644
index 00000000..920dc741
--- /dev/null
+++ b/kbackgammon/engines/offline/kbgoffline.cpp
@@ -0,0 +1,810 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+#include "kbgoffline.moc"
+#include "kbgoffline.h"
+
+#include <qlayout.h>
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qtimer.h>
+#include <qspinbox.h>
+#include <qwhatsthis.h>
+#include <qlineedit.h>
+#include <qvbox.h>
+
+#include <kapplication.h>
+#include <kmessagebox.h>
+#include <kiconloader.h>
+#include <kstdaction.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kmainwindow.h>
+#include <klineeditdlg.h>
+#include <kaction.h>
+#include <krandomsequence.h>
+#include <ktabctl.h>
+#include <stdlib.h>
+
+#include "version.h"
+
+class KBgEngineOfflinePrivate
+{
+public:
+
+ /*
+ * Various flags, representing the current status of the game
+ */
+ bool mRollFlag, mUndoFlag, mDoneFlag, mCubeFlag, mGameFlag, mRedoFlag;
+
+ /*
+ * Store two copies of the game: one backup and a working copy
+ */
+ KBgStatus mGame[2];
+
+ /*
+ * Use the standard method of obtaining random numbers
+ */
+ KRandomSequence *mRandom;
+
+ /*
+ * Game actions
+ */
+ KAction *mNew, *mSwap;
+ KToggleAction *mEdit;
+
+ /*
+ * Player's names
+ */
+ QString mName[2];
+
+ /*
+ * Who did the last roll
+ */
+ int mRoll;
+
+ /*
+ * How many checkers to move
+ */
+ int mMove;
+
+ /*
+ * Count the number of available undos
+ */
+ int mUndo;
+
+ /*
+ * Entry fields for the names
+ */
+ QLineEdit *mLe[2];
+
+};
+
+
+// == constructor, destructor and other ========================================
+
+/*
+ * Constructor
+ */
+KBgEngineOffline::KBgEngineOffline(QWidget *parent, QString *name, QPopupMenu *pmenu)
+ : KBgEngine(parent, name, pmenu)
+{
+ d = new KBgEngineOfflinePrivate();
+
+ /*
+ * get some entropy for the dice
+ */
+ d->mRandom = new KRandomSequence;
+ d->mRandom->setSeed(0);
+
+ /*
+ * Create engine specific actions
+ */
+ d->mNew = new KAction(i18n("&New Game..."), 0, this, SLOT(newGame()), this);
+ d->mSwap = new KAction(i18n("&Swap Colors"), 0, this, SLOT(swapColors()), this);
+
+ d->mEdit = new KToggleAction(i18n("&Edit Mode"), 0, this,
+ SLOT(toggleEditMode()), this);
+ d->mEdit->setChecked(false);
+
+ /*
+ * create & initialize the menu
+ */
+ d->mNew->plug(menu);
+ d->mEdit->plug(menu);
+ d->mSwap->plug(menu);
+
+ /*
+ * get standard board and set it
+ */
+ initGame();
+ emit newState(d->mGame[0]);
+
+ /*
+ * initialize the commit timeout
+ */
+ ct = new QTimer(this);
+ connect(ct, SIGNAL(timeout()), this, SLOT(done()));
+
+ /*
+ * internal statue variables
+ */
+ d->mRollFlag = d->mUndoFlag = d->mGameFlag = d->mDoneFlag = false;
+ connect(this, SIGNAL(allowCommand(int, bool)), this, SLOT(setAllowed(int, bool)));
+
+ /*
+ * Restore last stored settings
+ */
+ readConfig();
+}
+
+/*
+ * Destructor. The only child is the popup menu.
+ */
+KBgEngineOffline::~KBgEngineOffline()
+{
+ saveConfig();
+ delete d->mRandom;
+ delete d;
+}
+
+
+// == configuration handling ===================================================
+
+/*
+ * Put the engine specific details in the setup dialog
+ */
+void KBgEngineOffline::getSetupPages(KDialogBase *nb)
+{
+ /*
+ * Main Widget
+ */
+ QVBox *vbp = nb->addVBoxPage(i18n("Offline Engine"), i18n("Use this to configure the Offline engine"),
+ kapp->iconLoader()->loadIcon(PROG_NAME "_engine", KIcon::Desktop));
+
+ /*
+ * Get a multi page work space
+ */
+ KTabCtl *tc = new KTabCtl(vbp, "offline tabs");
+
+ /*
+ * Player names
+ */
+ QWidget *w = new QWidget(tc);
+ QGridLayout *gl = new QGridLayout(w, 2, 1, nb->spacingHint());
+
+ /*
+ * Group boxes
+ */
+ QGroupBox *gbn = new QGroupBox(i18n("Names"), w);
+
+ gl->addWidget(gbn, 0, 0);
+
+ gl = new QGridLayout(gbn, 2, 2, 20);
+
+ d->mLe[0] = new QLineEdit(d->mName[0], gbn);
+ d->mLe[1] = new QLineEdit(d->mName[1], gbn);
+
+ QLabel *lb[2];
+ lb[0] = new QLabel(i18n("First player:"), gbn);
+ lb[1] = new QLabel(i18n("Second player:"), gbn);
+
+ for (int i = 0; i < 2; i++) {
+ gl->addWidget(lb[i], i, 0);
+ gl->addWidget(d->mLe[i], i, 1);
+ }
+
+ QWhatsThis::add(d->mLe[0], i18n("Enter the name of the first player."));
+ QWhatsThis::add(d->mLe[1], i18n("Enter the name of the second player."));
+
+ /*
+ * Done with the page, put it in
+ */
+ gl->activate();
+ tc->addTab(w, i18n("&Player Names"));
+}
+
+/*
+ * Called when the setup dialog is positively closed
+ */
+void KBgEngineOffline::setupOk()
+{
+ d->mName[0] = d->mLe[0]->text();
+ d->mName[1] = d->mLe[1]->text();
+}
+void KBgEngineOffline::setupDefault()
+{
+ d->mName[0] = i18n("South");
+ d->mName[1] = i18n("North");
+}
+void KBgEngineOffline::setupCancel()
+{
+ // do nothing
+}
+
+/*
+ * Restore settings
+ */
+void KBgEngineOffline::readConfig()
+{
+ KConfig* config = kapp->config();
+ config->setGroup("offline engine");
+
+ d->mName[0] = config->readEntry("player-one", i18n("South")); // same as above
+ d->mName[1] = config->readEntry("player-two", i18n("North")); // same as above
+ cl = config->readNumEntry("timer", 2500);
+}
+
+/*
+ * Save the engine specific settings
+ */
+void KBgEngineOffline::saveConfig()
+{
+ KConfig* config = kapp->config();
+ config->setGroup("offline engine");
+
+ config->writeEntry("player-one", d->mName[0] );
+ config->writeEntry("player-two", d->mName[1]);
+ config->writeEntry("timer", cl);
+}
+
+
+// == start and init games =====================================================
+
+/*
+ * Start a new game.
+ */
+void KBgEngineOffline::newGame()
+{
+ int u = 0;
+ int t = 0;
+
+ /*
+ * If there is a game running we warn the user first
+ */
+ if (d->mGameFlag && (KMessageBox::warningYesNo((QWidget *)parent(),
+ i18n("A game is currently in progress. "
+ "Starting a new one will terminate it."),
+ QString::null, i18n("Start New Game"),
+ i18n("Continue Old Game"))
+ == KMessageBox::No))
+ return;
+
+ /*
+ * Separate from the previous game
+ */
+ emit infoText("<br/><br/><br/>");
+
+ /*
+ * Get player's names - user can still cancel
+ */
+ if (!queryPlayerName(US) || !queryPlayerName(THEM))
+ return;
+
+ /*
+ * let the games begin
+ */
+ d->mGameFlag = true;
+
+ /*
+ * Initialize the board
+ */
+ initGame();
+
+ /*
+ * Figure out who starts by rolling
+ */
+ while (u == t) {
+ u = getRandom();
+ t = getRandom();
+ emit infoText(i18n("%1 rolls %2, %3 rolls %4.").
+ arg(d->mName[0]).arg(u).arg(d->mName[1]).arg(t));
+ }
+
+ if (u > t) {
+ emit infoText(i18n("%1 makes the first move.").arg(d->mName[0]));
+ d->mRoll = US;
+ } else {
+ emit infoText(i18n("%1 makes the first move.").arg(d->mName[1]));
+ d->mRoll = THEM;
+ int n = u; u = t; t = n;
+ }
+
+ /*
+ * set the dice and tell the board
+ */
+ rollDiceBackend(d->mRoll, u, t);
+
+ /*
+ * tell the user
+ */
+ emit statText(i18n("%1 vs. %2").arg(d->mName[0]).arg(d->mName[1]));
+}
+
+/*
+ * Initialize the state descriptors mGame[0|1]
+ */
+void KBgEngineOffline::initGame()
+{
+ /*
+ * nobody rolled yet
+ */
+ d->mRoll = -1;
+
+ /*
+ * set up a standard game
+ */
+ d->mGame[0].setCube(1, true, true);
+ d->mGame[0].setDirection(+1);
+ d->mGame[0].setColor(+1);
+ for (int i = 1; i < 25; i++)
+ d->mGame[0].setBoard(i, US, 0);
+ d->mGame[0].setBoard( 1, US, 2); d->mGame[0].setBoard( 6, THEM, 5);
+ d->mGame[0].setBoard( 8, THEM, 3); d->mGame[0].setBoard(12, US, 5);
+ d->mGame[0].setBoard(13, THEM, 5); d->mGame[0].setBoard(17, US, 3);
+ d->mGame[0].setBoard(19, US, 5); d->mGame[0].setBoard(24, THEM, 2);
+ d->mGame[0].setHome(US, 0); d->mGame[0].setHome(THEM, 0);
+ d->mGame[0].setBar(US, 0); d->mGame[0].setBar(THEM, 0);
+ d->mGame[0].setDice(US , 0, 0); d->mGame[0].setDice(US , 1, 0);
+ d->mGame[0].setDice(THEM, 0, 0); d->mGame[0].setDice(THEM, 1, 0);
+
+ /*
+ * save backup of the game state
+ */
+ d->mGame[1] = d->mGame[0];
+
+ emit allowCommand(Load, true);
+}
+
+/*
+ * Open a dialog to query for the name of player w. Return true unless
+ * the dialog was canceled.
+ */
+bool KBgEngineOffline::queryPlayerName(int w)
+{
+ bool ret = false;
+ QString *name;
+ QString text;
+
+ if (w == US) {
+ name = &d->mName[0];
+ text = i18n("Please enter the nickname of the player whose home\n"
+ "is in the lower half of the board:");
+ } else {
+ name = &d->mName[1];
+ text = i18n("Please enter the nickname of the player whose home\n"
+ "is in the upper half of the board:");
+ }
+
+ do {
+ *name = KLineEditDlg::getText(text, *name, &ret, (QWidget *)parent());
+ if (!ret) break;
+
+ } while (name->isEmpty());
+
+ return ret;
+}
+
+
+// == moving ===================================================================
+
+/*
+ * Finish the last move - called by the timer and directly by the used
+ */
+void KBgEngineOffline::done()
+{
+ ct->stop();
+
+ emit allowMoving(false);
+ emit allowCommand(Done, false);
+ emit allowCommand(Undo, false);
+
+ if (abs(d->mGame[0].home(d->mRoll)) == 15) {
+
+ emit infoText(i18n("%1 wins the game. Congratulations!").
+ arg((d->mRoll == US) ? d->mName[0] : d->mName[1]));
+ d->mGameFlag = false;
+ emit allowCommand(Roll, false);
+ emit allowCommand(Cube, false);
+
+ } else {
+
+ emit allowCommand(Roll, true);
+ if (d->mGame[0].cube((d->mRoll == US ? THEM : US)) > 0) {
+
+ d->mGame[0].setDice(US , 0, 0); d->mGame[0].setDice(US , 1, 0);
+ d->mGame[0].setDice(THEM, 0, 0); d->mGame[0].setDice(THEM, 1, 0);
+
+ emit newState(d->mGame[0]);
+ emit getState(&d->mGame[0]);
+
+ d->mGame[1] = d->mGame[0];
+
+ emit infoText(i18n("%1, please roll or double.").
+ arg((d->mRoll == THEM) ? d->mName[0] : d->mName[1]));
+ emit allowCommand(Cube, true);
+
+ } else {
+
+ roll();
+ emit allowCommand(Cube, false);
+ }
+ }
+}
+
+/*
+ * Undo the last move
+ */
+void KBgEngineOffline::undo()
+{
+ ct->stop();
+
+ d->mRedoFlag = true;
+ ++d->mUndo;
+
+ emit allowMoving(true);
+ emit allowCommand(Done, false);
+ emit allowCommand(Redo, true);
+ emit undoMove();
+}
+
+/*
+ * Redo the last move
+ */
+void KBgEngineOffline::redo()
+{
+ --d->mUndo;
+ emit redoMove();
+}
+
+/*
+ * Take the move string and make the changes on the working copy
+ * of the state.
+ */
+void KBgEngineOffline::handleMove(QString *s)
+{
+ int index = 0;
+ QString t = s->mid(index, s->find(' ', index));
+ index += 1 + t.length();
+ int moves = t.toInt();
+
+ /*
+ * Allow undo and possibly start the commit timer
+ */
+ d->mRedoFlag &= ((moves < d->mMove) && (d->mUndo > 0));
+ emit allowCommand(Undo, moves > 0);
+ emit allowCommand(Redo, d->mRedoFlag);
+ emit allowCommand(Done, moves == d->mMove);
+ if (moves == d->mMove && cl) {
+ emit allowMoving(false);
+ ct->start(cl, true);
+ }
+
+ /*
+ * Apply moves to d->mGame[1] and store results in d->mGame[0]
+ */
+ d->mGame[0] = d->mGame[1];
+
+ /*
+ * process each individual move
+ */
+ for (int i = 0; i < moves; i++) {
+ bool kick = false;
+ t = s->mid(index, s->find(' ', index) - index);
+ index += 1 + t.length();
+ char c = '-';
+ if (t.contains('+')) {
+ c = '+';
+ kick = true;
+ }
+ QString r = t.left(t.find(c));
+ if (r.contains("bar")) {
+ d->mGame[0].setBar(d->mRoll, abs(d->mGame[0].bar(d->mRoll)) - 1);
+ } else {
+ int from = r.toInt();
+ d->mGame[0].setBoard(from, d->mRoll, abs(d->mGame[0].board(from)) - 1);
+ }
+ t.remove(0, 1 + r.length());
+ if (t.contains("off")) {
+ d->mGame[0].setHome(d->mRoll, abs(d->mGame[0].home(d->mRoll)) + 1);
+ } else {
+ int to = t.toInt();
+ if (kick) {
+ d->mGame[0].setBoard(to, d->mRoll, 0);
+ int el = ((d->mRoll == US) ? THEM : US);
+ d->mGame[0].setBar(el, abs(d->mGame[0].bar(el)) + 1);
+ }
+ d->mGame[0].setBoard(to, d->mRoll, abs(d->mGame[0].board(to)) + 1);
+ }
+ }
+}
+
+
+// == dice & rolling ===========================================================
+
+/*
+ * Roll random dice for the player whose turn it is
+ */
+void KBgEngineOffline::roll()
+{
+ rollDice((d->mRoll == US) ? THEM : US);
+}
+
+/*
+ * If possible, roll random dice for player w
+ */
+void KBgEngineOffline::rollDice(const int w)
+{
+ if ((d->mRoll != w) && d->mRollFlag) {
+ rollDiceBackend(w, getRandom(), getRandom());
+ return;
+ }
+ emit infoText(i18n("It's not your turn to roll!"));
+}
+
+/*
+ * Return a random integer between 1 and 6. According to the man
+ * page of rand(), this is the way to go...
+ */
+int KBgEngineOffline::getRandom()
+{
+ return 1+d->mRandom->getLong(6);
+}
+
+/*
+ * Set the dice for player w to a and b. Reload the board and determine the
+ * maximum number of moves
+ */
+void KBgEngineOffline::rollDiceBackend(const int w, const int a, const int b)
+{
+ /*
+ * This is a special case that stems from leaving the edit
+ * mode.
+ */
+ if (a == 0)
+ return;
+
+ /*
+ * Set the dice and tel the board about the new state
+ */
+ d->mGame[0].setDice(w, 0, a);
+ d->mGame[0].setDice(w, 1, b);
+ d->mGame[0].setDice((w == US) ? THEM : US, 0, 0);
+ d->mGame[0].setDice((w == US) ? THEM : US, 1, 0);
+ d->mGame[0].setTurn(w);
+
+ d->mGame[1] = d->mGame[0];
+
+ d->mRoll = w;
+ emit newState(d->mGame[0]);
+
+ /*
+ * No more roling until Done and no Undo yet
+ */
+ emit allowCommand(Undo, false);
+ emit allowCommand(Roll, false);
+ d->mRedoFlag = false;
+ d->mUndo = 0;
+
+ /*
+ * Tell the players how many checkers to move
+ */
+ switch (d->mMove = d->mGame[0].moves()) {
+ case -1:
+ emit infoText(i18n("Game over!"));
+ d->mGameFlag = false;
+ emit allowCommand(Roll, false);
+ emit allowCommand(Cube, false);
+ emit allowMoving(false);
+ break;
+ case 0:
+ emit infoText(i18n("%1, you cannot move.").
+ arg((w == US) ? d->mName[0] : d->mName[1]));
+ if (cl)
+ ct->start(cl, true);
+ emit allowMoving(false);
+ break;
+// case 1:
+ default:
+ emit infoText(QString((w == US) ? d->mName[0] : d->mName[1]) +
+ i18n(", please move 1 piece.",", please move %n pieces.",d->mMove));
+ emit allowMoving(true);
+ break;
+ }
+}
+
+
+// == cube =====================================================================
+
+/*
+ * Double the cube for the player that can double - asks player
+ */
+void KBgEngineOffline::cube()
+{
+ int w = ((d->mRoll == US) ? THEM : US);
+
+ if (d->mRollFlag && d->mGame[0].cube(w) > 0) {
+ emit allowCommand(Cube, false);
+ if (KMessageBox::questionYesNo((QWidget *)parent(),
+ i18n("%1 has doubled. %2, do you accept the double?").
+ arg((w == THEM) ? d->mName[1] : d->mName[0]).
+ arg((w == US) ? d->mName[1] : d->mName[0]),
+ i18n("Doubling"), i18n("Accept"), i18n("Reject")) != KMessageBox::Yes) {
+ d->mGameFlag = false;
+ emit allowCommand(Roll, false);
+ emit allowCommand(Cube, false);
+ emit infoText(i18n("%1 wins the game. Congratulations!").
+ arg((w == US) ? d->mName[0] : d->mName[1]));
+ return;
+ }
+
+ emit infoText(i18n("%1 has accepted the double. The game continues.").
+ arg((w == THEM) ? d->mName[0] : d->mName[1]));
+
+ if (d->mGame[0].cube(US)*d->mGame[0].cube(THEM) > 0)
+ d->mGame[0].setCube(2, w == THEM, w == US);
+ else
+ d->mGame[0].setCube(2*d->mGame[0].cube(w), w == THEM, w == US);
+
+ emit newState(d->mGame[0]);
+ emit getState(&d->mGame[0]);
+
+ d->mGame[1] = d->mGame[0];
+
+ roll();
+ }
+}
+
+/*
+ * Double the cube for player w
+ */
+void KBgEngineOffline::doubleCube(const int)
+{
+ cube();
+}
+
+
+// == various slots & functions ================================================
+
+/*
+ * Check with the user if we should really quit in the middle of a
+ * game.
+ */
+bool KBgEngineOffline::queryClose()
+{
+ if (!d->mGameFlag)
+ return true;
+
+ switch (KMessageBox::warningContinueCancel((QWidget *)parent(),
+ i18n("In the middle of a game. "
+ "Really quit?"), QString::null, KStdGuiItem::quit())) {
+ case KMessageBox::Continue :
+ return TRUE;
+ case KMessageBox::Cancel :
+ return FALSE;
+ default: // cancel
+ return FALSE;
+ }
+ return true;
+}
+
+/*
+ * Quitting is fine at any time
+ */
+bool KBgEngineOffline::queryExit()
+{
+ return true;
+}
+
+/*
+ * Handle textual commands. Right now, all commands are ignored
+ */
+void KBgEngineOffline::handleCommand(const QString& cmd)
+{
+ emit infoText(i18n("Text commands are not yet working. "
+ "The command '%1' has been ignored.").arg(cmd));
+}
+
+/*
+ * Load the last known sane state of the board
+ */
+void KBgEngineOffline::load()
+{
+ if (d->mEdit->isChecked())
+ emit newState(d->mGame[1]);
+ else {
+ // undo up to four moves
+ undo();
+ undo();
+ undo();
+ undo();
+ }
+}
+
+/*
+ * Store if cmd is allowed or not
+ */
+void KBgEngineOffline::setAllowed(int cmd, bool f)
+{
+ switch (cmd) {
+ case Roll:
+ d->mRollFlag = f;
+ return;
+ case Undo:
+ d->mUndoFlag = f;
+ return;
+ case Cube:
+ d->mCubeFlag = f;
+ return;
+ case Done:
+ d->mDoneFlag = f;
+ return;
+ }
+}
+
+/*
+ * Swaps the used colors on the board
+ */
+void KBgEngineOffline::swapColors()
+{
+ d->mGame[1].setDice(US, 0, d->mGame[0].dice(US, 0));
+ d->mGame[1].setDice(US, 1, d->mGame[0].dice(US, 1));
+ d->mGame[1].setDice(THEM, 0, d->mGame[0].dice(THEM, 0));
+ d->mGame[1].setDice(THEM, 1, d->mGame[0].dice(THEM, 1));
+ d->mGame[1].setColor(d->mGame[1].color(THEM), US);
+ emit newState(d->mGame[1]);
+ emit getState(&d->mGame[1]);
+ d->mGame[0] = d->mGame[1];
+}
+
+/*
+ * Switch back and forth between edit and play mode
+ */
+void KBgEngineOffline::toggleEditMode()
+{
+ emit setEditMode(d->mEdit->isChecked());
+ if (d->mEdit->isChecked()) {
+ ct->stop();
+ d->mNew->setEnabled(false);
+ d->mSwap->setEnabled(false);
+ emit allowCommand(Undo, false);
+ emit allowCommand(Roll, false);
+ emit allowCommand(Done, false);
+ emit allowCommand(Cube, false);
+ emit statText(i18n("%1 vs. %2 - Edit Mode").arg(d->mName[0]).arg(d->mName[1]));
+ } else {
+ d->mNew->setEnabled(true);
+ d->mSwap->setEnabled(true);
+ emit statText(i18n("%1 vs. %2").arg(d->mName[0]).arg(d->mName[1]));
+ emit getState(&d->mGame[1]);
+ d->mGame[0] = d->mGame[1];
+ emit allowCommand(Done, d->mDoneFlag);
+ emit allowCommand(Cube, d->mCubeFlag);
+ emit allowCommand(Undo, d->mUndoFlag);
+ emit allowCommand(Roll, d->mRollFlag);
+ int w =((d->mGame[0].dice(US, 0) && d->mGame[0].dice(US, 1)) ? US : THEM);
+ rollDiceBackend(w, d->mGame[0].dice(w, 0), d->mGame[0].dice(w, 1));
+ }
+}
+
+// EOF
diff --git a/kbackgammon/engines/offline/kbgoffline.h b/kbackgammon/engines/offline/kbgoffline.h
new file mode 100644
index 00000000..db2bdc03
--- /dev/null
+++ b/kbackgammon/engines/offline/kbgoffline.h
@@ -0,0 +1,213 @@
+/* Yo Emacs, this -*- C++ -*-
+
+ Copyright (C) 1999-2001 Jens Hoefkens
+ jens@hoefkens.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ $Id$
+
+*/
+
+#ifndef __KBGOFFLINE_H
+#define __KBGOFFLINE_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <generic/kbgengine.h>
+
+#include "kbgboard.h"
+#include "kbgstatus.h"
+
+class KBgEngineOfflinePrivate;
+
+/**
+ *
+ * The interface of an offline backgammon engine. The engine is inherently
+ * stupid and doesn't play - it just manages the games betweeen two humans
+ * sitting at the same computer. Network enabled games will be part of the
+ * next generation engine (KBgNg).
+ *
+ * @short The offline backgammon engine
+ * @author Jens Hoefkens <jens@hoefkens.com>
+ *
+ */
+class KBgEngineOffline : public KBgEngine
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * Constructor
+ */
+ KBgEngineOffline(QWidget *parent = 0, QString *name = 0, QPopupMenu *pmenu = 0);
+
+ /**
+ * Destructor
+ */
+ virtual ~KBgEngineOffline();
+
+ /**
+ * Fills the engine-specific page into the notebook
+ */
+ virtual void getSetupPages(KDialogBase *nb);
+
+ /**
+ * Save new steup
+ */
+ virtual void setupOk();
+
+ /**
+ * Load default setup
+ */
+ virtual void setupDefault();
+
+ /**
+ * Cancel the changes to the setup
+ */
+ virtual void setupCancel();
+
+ /**
+ * Check with the engine if we can quit. This may require user
+ * interaction.
+ */
+ virtual bool queryClose();
+
+ /**
+ * About to be closed. Let the engine exit properly.
+ */
+ virtual bool queryExit();
+
+public slots:
+
+ /**
+ * Read user settings from the config file
+ */
+ virtual void readConfig();
+
+ /**
+ * Save user settings to the config file
+ */
+ virtual void saveConfig();
+
+ /**
+ * Roll dice for the player w
+ */
+ virtual void rollDice(const int w);
+
+ /**
+ * Double the cube of player w
+ */
+ virtual void doubleCube(const int w);
+
+ /**
+ * A move has been made on the board - see the board class
+ * for the format of the string s
+ */
+ virtual void handleMove(QString *s);
+
+ /**
+ * Undo the last move
+ */
+ virtual void undo();
+
+ /**
+ * Redo the last move
+ */
+ virtual void redo();
+
+ /**
+ * Roll dice for whoevers turn it is
+ */
+ virtual void roll();
+
+ /**
+ * Double the cube for whoevers can double right now
+ */
+ virtual void cube();
+
+ /**
+ * Reload the board to the last known sane state
+ */
+ virtual void load();
+
+ /**
+ * Commit a move
+ */
+ virtual void done();
+
+ /**
+ * Process the string cmd
+ */
+ virtual void handleCommand(const QString& cmd);
+
+ /**
+ * Start a new game.
+ */
+ virtual void newGame();
+ virtual bool haveNewGame() {return true;}
+
+
+protected slots:
+
+ /**
+ * Initialize the state descriptors game[0] and game[1]
+ */
+ void initGame();
+
+ /**
+ * Switch back and forth between edit and play mode
+ */
+ void toggleEditMode();
+
+ /**
+ * Store if cmd is allowed or not
+ */
+ void setAllowed(int cmd, bool f);
+
+ /**
+ * Swaps the used colors on the board
+ */
+ void swapColors();
+
+protected:
+
+ /**
+ * Returns a random integer between 1 and 6
+ */
+ int getRandom();
+
+ /**
+ * Set the dice for player w to a and b. Reload the board and determine the
+ * maximum number of moves
+ */
+ void rollDiceBackend(const int w, const int a, const int b);
+
+ /**
+ * Open a dialog to query for the name of player w. Return true unless
+ * the dialog was canceled.
+ */
+ bool queryPlayerName(int w);
+
+private:
+
+ KBgEngineOfflinePrivate *d;
+
+};
+
+#endif // __KBGOFFLINE_H