/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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("" + text + ""); } else emit infoText("" + text + "
"); 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") + "
"); } /* * 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 + "

"); emit infoText(i18n("Disconnected.") + "
"); 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: 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], "<"); line.replace(pat[HTML_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("
") + line + "

"; 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 += "
    " + line; if (line.contains(pat[WhoisE1]) || line.contains(pat[WhoisE2])) { rxStatus = RxNormal; emit infoText("" + rxCollect + "
"); } } /* * Handle messages during the RxRating state */ void KBgEngineFIBS::handleMessageRating(const QString &line) { rxCollect += "
" + line; if (line.contains(pat[EndRate]) && ++rxCount == 2) { emit infoText("" + rxCollect + "
"); rxStatus = RxNormal; } } /* * Handle messages during the RxMotd state */ void KBgEngineFIBS::handleMessageMotd(const QString &line) { if (line.contains(pat[MotdEnd])) { rxStatus = RxNormal; emit infoText("
" + rxCollect + "
"); /* * 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], "

"); tline.replace(pat[BoxVer1], ""); tline.replace(pat[BoxVer2], ""); rxCollect += "
" + 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("
" + rxCollect + "

"); 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("

" + 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("" + i18n("The moreboards toggle has been set.") + ""); emit serverString("toggle moreboards"); } if (words >= 22 && n[8] != 1) { /* * need to know who logs out */ emit infoText("" + i18n("The notify toggle has been set.") + ""); emit serverString("toggle notify"); } if (words >= 22 && n[11] != 1) { /* * want to know who starts playing games */ emit infoText("" + i18n("The report toggle has been set.") + ""); 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("") + rxCollect + ""); rxCollect = ""; QString tmp = rawline; handleServerData(tmp); return; } /* * Still in the middle of the login sequence, still collecting information */ rxCollect += "
" + 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 %1. 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("

" + text + "

"); 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 = "" + line + ""; } // -------------------------------------------------------------------------------- /* * 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 += "
  
" + 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 = "" + line + ""; } else if (line.contains(pat[DoubleY]) || line.contains(pat[DoubleN])) { fibsOpt[OptDouble]->setChecked(line.contains(pat[DoubleY])); line = "" + line + ""; } else if (line.contains(pat[RatingY]) || line.contains(pat[RatingN])) { fibsOpt[OptRatings]->setChecked(line.contains(pat[RatingY])); line = "" + line + ""; } // -------------------------------------------------------------------------------- /* * 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("
") + line + ""; return; } else if (line.contains(pat[MotdBeg])) { rxStatus = RxMotd; rxCollect = ""; return; } else if (line.contains(pat[BegRate])) { rxStatus = RxRating; rxCount = 0; rxCollect = "
" + line; return; } else if (line.contains(pat[Goodbye])) { rxStatus = RxGoodbye; rxCollect = "


"; 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("
  
" + 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 = "" + line + ""; } else if (line.contains(pat[EndBlnd])) { rawline.replace(pat[EndBlnd], ""); rawline.truncate(rawline.length()-1); emit changePlayerStatus(rawline, KFibsPlayerList::Blind, false); line = "" + line + ""; } /* * 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 += "
  
" + 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("
") + 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.") + "
"); 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 = "" + line + ""; } // -------------------------------------------------------------------------------- /* * 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; }