summaryrefslogtreecommitdiffstats
path: root/libksirtet
diff options
context:
space:
mode:
Diffstat (limited to 'libksirtet')
-rw-r--r--libksirtet/CHANGELOG1
-rw-r--r--libksirtet/DESIGN57
-rw-r--r--libksirtet/LICENSE22
-rw-r--r--libksirtet/Makefile.am4
-rw-r--r--libksirtet/README16
-rw-r--r--libksirtet/README.gtetris21
-rw-r--r--libksirtet/TODO15
-rw-r--r--libksirtet/base/Makefile.am16
-rw-r--r--libksirtet/base/README1
-rw-r--r--libksirtet/base/baseprefs.kcfgc7
-rw-r--r--libksirtet/base/board.cpp429
-rw-r--r--libksirtet/base/board.h134
-rw-r--r--libksirtet/base/factory.cpp52
-rw-r--r--libksirtet/base/factory.h60
-rw-r--r--libksirtet/base/field.cpp162
-rw-r--r--libksirtet/base/field.h66
-rw-r--r--libksirtet/base/gtetris.cpp241
-rw-r--r--libksirtet/base/gtetris.h178
-rw-r--r--libksirtet/base/highscores.cpp19
-rw-r--r--libksirtet/base/highscores.h13
-rw-r--r--libksirtet/base/inter.cpp15
-rw-r--r--libksirtet/base/inter.h23
-rw-r--r--libksirtet/base/kzoommainwindow.cpp115
-rw-r--r--libksirtet/base/kzoommainwindow.h128
-rw-r--r--libksirtet/base/libksirtet1.kcfg38
-rw-r--r--libksirtet/base/main.cpp131
-rw-r--r--libksirtet/base/main.h49
-rw-r--r--libksirtet/base/piece.cpp274
-rw-r--r--libksirtet/base/piece.h155
-rw-r--r--libksirtet/base/settings.cpp84
-rw-r--r--libksirtet/base/settings.h33
-rw-r--r--libksirtet/common/Makefile.am28
-rw-r--r--libksirtet/common/README1
-rw-r--r--libksirtet/common/ai.cpp356
-rw-r--r--libksirtet/common/ai.h122
-rw-r--r--libksirtet/common/board.cpp286
-rw-r--r--libksirtet/common/board.h75
-rw-r--r--libksirtet/common/commonprefs.kcfgc9
-rw-r--r--libksirtet/common/factory.cpp28
-rw-r--r--libksirtet/common/factory.h36
-rw-r--r--libksirtet/common/field.cpp243
-rw-r--r--libksirtet/common/field.h68
-rw-r--r--libksirtet/common/highscores.cpp60
-rw-r--r--libksirtet/common/highscores.h20
-rw-r--r--libksirtet/common/inter.cpp150
-rw-r--r--libksirtet/common/inter.h62
-rw-r--r--libksirtet/common/libksirtet2.kcfg36
-rw-r--r--libksirtet/common/main.cpp60
-rw-r--r--libksirtet/common/main.h22
-rw-r--r--libksirtet/common/misc_ui.cpp194
-rw-r--r--libksirtet/common/misc_ui.h81
-rw-r--r--libksirtet/common/settings.cpp54
-rw-r--r--libksirtet/common/settings.h28
-rw-r--r--libksirtet/common/types.cpp16
-rw-r--r--libksirtet/common/types.h26
-rw-r--r--libksirtet/configure.in.in5
-rw-r--r--libksirtet/lib/CHANGELOG39
-rw-r--r--libksirtet/lib/LICENSE18
-rw-r--r--libksirtet/lib/Makefile.am21
-rw-r--r--libksirtet/lib/README63
-rw-r--r--libksirtet/lib/TODO18
-rw-r--r--libksirtet/lib/defines.cpp24
-rw-r--r--libksirtet/lib/defines.h35
-rw-r--r--libksirtet/lib/internal.cpp278
-rw-r--r--libksirtet/lib/internal.h152
-rw-r--r--libksirtet/lib/keys.cpp104
-rw-r--r--libksirtet/lib/keys.h42
-rw-r--r--libksirtet/lib/libksirtet_export.h35
-rw-r--r--libksirtet/lib/meeting.cpp575
-rw-r--r--libksirtet/lib/meeting.h137
-rw-r--r--libksirtet/lib/miscui.cpp58
-rw-r--r--libksirtet/lib/miscui.h43
-rw-r--r--libksirtet/lib/mp_board.h51
-rw-r--r--libksirtet/lib/mp_interface.cpp274
-rw-r--r--libksirtet/lib/mp_interface.h246
-rw-r--r--libksirtet/lib/mp_option.h74
-rw-r--r--libksirtet/lib/mp_simple_board.cpp84
-rw-r--r--libksirtet/lib/mp_simple_board.h45
-rw-r--r--libksirtet/lib/mp_simple_interface.cpp152
-rw-r--r--libksirtet/lib/mp_simple_interface.h48
-rw-r--r--libksirtet/lib/mp_simple_types.cpp6
-rw-r--r--libksirtet/lib/mp_simple_types.h36
-rw-r--r--libksirtet/lib/pline.cpp147
-rw-r--r--libksirtet/lib/pline.h112
-rw-r--r--libksirtet/lib/smanager.cpp115
-rw-r--r--libksirtet/lib/smanager.h88
-rw-r--r--libksirtet/lib/socket.cpp80
-rw-r--r--libksirtet/lib/socket.h65
-rw-r--r--libksirtet/lib/types.cpp254
-rw-r--r--libksirtet/lib/types.h197
-rw-r--r--libksirtet/lib/version.h6
-rw-r--r--libksirtet/lib/wizard.cpp229
-rw-r--r--libksirtet/lib/wizard.h57
93 files changed, 8633 insertions, 0 deletions
diff --git a/libksirtet/CHANGELOG b/libksirtet/CHANGELOG
new file mode 100644
index 00000000..d7e56fd2
--- /dev/null
+++ b/libksirtet/CHANGELOG
@@ -0,0 +1 @@
+see "ksirtet" CHANGELOG
diff --git a/libksirtet/DESIGN b/libksirtet/DESIGN
new file mode 100644
index 00000000..f09b8539
--- /dev/null
+++ b/libksirtet/DESIGN
@@ -0,0 +1,57 @@
+'lo, time to explain how some things are designed in the generic class
+of these games (ksirtet, kfouleggs and klickety)
+
+NB: You should read this file with a fixed font editor ...
+
+the following set of functions is implemented in the hierarchy of classes :
+GenericTetris -> BaseBoard -> Board -> specific Board class for each game
+
+===============================================================================
+action 'Piece Drop Down' activated
+ |
+pieceDropped() <-|
+ | |
+oneLineDown() -> by timer -|
+
+===============================================================================
+if oneLineDown() make the piece touch the ground
+ |
+pieceDropped()
+ |
+_beforeGlue() <-|
+ | |
+beforeGlue() -> by timer -| // here is implemented the bump effect
+ | (when done)
+gluePiece()
+ |
+_afterGlue() <-|
+ | |
+afterGlue() -> by timer -| // here kfouleggs remove holes
+ | (when done)
+needRemoving ? no -> _afterAfterRemove()
+ |
+_beforeRemove() <-|
+ | |
+beforeRemove() -> by timer -| // here blocks to be removed are highlighted
+ | (when done)
+remove()
+ |
+_afterRemove() <-|
+ | |
+afterRemove() -> by timer -| // here is animated the fall of piece in
+ | // the holes leaved by removed blocks
+ | if needs removing again -> _beforeRemove()
+ |
+_afterAfterRemove() ? no gift pending -> afterAfterRemove()
+ |
+putGift()
+ |
+_afterGift() <-|
+ | |
+afterGift() -> by timer -| // here kfouleggs make the gift block to land
+ |
+_afterAfterRemove()
+ |
+afterAfterRemove() ? gameOver -> gameOver()
+ |
+newPiece()
diff --git a/libksirtet/LICENSE b/libksirtet/LICENSE
new file mode 100644
index 00000000..d2793d2f
--- /dev/null
+++ b/libksirtet/LICENSE
@@ -0,0 +1,22 @@
+LIBKSIRTET
+----------
+Copyright (c) 1995 Eirik ENG
+Copyright (c) 1996-2004 Nicolas HADACEK (hadacek@kde.org)
+
+
+This file is part of the KDE ksirtet library
+Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License version 2 as published by the Free Software Foundation.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public License
+along with this library; see the file COPYING.LIB. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.
diff --git a/libksirtet/Makefile.am b/libksirtet/Makefile.am
new file mode 100644
index 00000000..14913f02
--- /dev/null
+++ b/libksirtet/Makefile.am
@@ -0,0 +1,4 @@
+SUBDIRS = base lib common
+
+messages:
+ $(XGETTEXT) base/*.cpp lib/*.cpp common/*.cpp -o $(podir)/libksirtet.pot
diff --git a/libksirtet/README b/libksirtet/README
new file mode 100644
index 00000000..f6f9b187
--- /dev/null
+++ b/libksirtet/README
@@ -0,0 +1,16 @@
+LIBKSIRTET: a set of libraries for use in tetris-alike games
+------------------------------------------------------------
+Copyright (c) 1995 Eirik ENG
+Copyright (c) 1996-2004 Nicolas HADACEK (hadacek@kde.org)
+Distributed under the GNU Library General Public License
+
+
+These libraries provides almost all functionnalities to make
+a tetris-alike games. Moreover it gives multiplayers functionality
+and a framework for "artificial intelligence" players.
+
+The basis of the core code (gtetris) has been written by Eirik Eng as
+an example for the Qt distribution (see "README.gtetris").
+
+
+Enjoy !
diff --git a/libksirtet/README.gtetris b/libksirtet/README.gtetris
new file mode 100644
index 00000000..88d6f314
--- /dev/null
+++ b/libksirtet/README.gtetris
@@ -0,0 +1,21 @@
+ Oslo, May 14 1995
+
+This code was written about 1 and a half year ago to test the very
+first Qt skeleton. It was in fact the first Qt application and was
+written at a time when Qt only had pushbutton widgets and the drawing
+engine was limited to drawing text in a single font and drawing lines
+(no kidding, those were the only two drawing operations). In fact the
+tetris project doubled the number of widgets in Qt by introducing
+the QLCDNumber widget. The whole application was written in 5 evenings
+and 1 weekend and is not very well documented. The tetris engine is
+implemented in the GenericTetris class and was first made on my
+good old 10 MHZ AT where I made a DOS text-mode Tetris (using about
+50 lines) to test it out, it was later moved to a SUN Sparc 10, where it
+met Qt. GenericTetris is totally independent of Qt, feel free to use
+it in any way you like. (see the file gtetris.h for a brief description
+of how to use it)
+
+Enjoy!
+
+ Eirik Eng
+ -----
diff --git a/libksirtet/TODO b/libksirtet/TODO
new file mode 100644
index 00000000..abe0c12d
--- /dev/null
+++ b/libksirtet/TODO
@@ -0,0 +1,15 @@
+GLOBAL:
+ * options : different types of tiles ?
+ * background image + more animations
+ * key profiles (?)
+ * autopause if focus is lost (cf kmines)
+ * set the keyboard repeat rate (possible ?) or better do not use the
+ repeat...
+ * use KGame
+
+
+KNOWN BUGS:
+ * occasional crash in multiplayer wizard / with keybinding changes.
+ * infinite loop with out-of-bound call to GArray::at
+ sometimes when creating several boards (human vs AI/human).
+ * does not update immediately second board when changing background.
diff --git a/libksirtet/base/Makefile.am b/libksirtet/base/Makefile.am
new file mode 100644
index 00000000..bb8b2365
--- /dev/null
+++ b/libksirtet/base/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes)
+
+# Don't compile with hidden symbols since we are a library.
+if disable_VISIBILITY
+KDE_CXXFLAGS = -fvisibility=default
+endif
+
+noinst_LTLIBRARIES = libksirtetbase.la
+libksirtetbase_la_LDFLAGS = $(all_libraries)
+
+noinst_HEADERS = kzoommainwindow.h piece.h gtetris.h factory.h highscores.h \
+ board.h settings.h field.h inter.h main.h
+libksirtetbase_la_SOURCES = kzoommainwindow.cpp main.cpp field.cpp piece.cpp highscores.cpp \
+ factory.cpp gtetris.cpp board.cpp settings.cpp \
+ inter.cpp baseprefs.kcfgc
+METASOURCES = AUTO
diff --git a/libksirtet/base/README b/libksirtet/base/README
new file mode 100644
index 00000000..a16060b1
--- /dev/null
+++ b/libksirtet/base/README
@@ -0,0 +1 @@
+This directory contains code shared between ksirtet, kfouleggs and klickety.
diff --git a/libksirtet/base/baseprefs.kcfgc b/libksirtet/base/baseprefs.kcfgc
new file mode 100644
index 00000000..41c852af
--- /dev/null
+++ b/libksirtet/base/baseprefs.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=libksirtet1.kcfg
+#IncludeFiles=defines.h
+ClassName=BasePrefs
+Singleton=true
+#CustomAdditions=true
+Mutators=MenubarVisible,BlockSize
diff --git a/libksirtet/base/board.cpp b/libksirtet/base/board.cpp
new file mode 100644
index 00000000..257e72c3
--- /dev/null
+++ b/libksirtet/base/board.cpp
@@ -0,0 +1,429 @@
+#include "board.h"
+#include "board.moc"
+
+#include <knotifyclient.h>
+#include <klocale.h>
+#include <kzoommainwindow.h>
+
+#include "piece.h"
+#include "factory.h"
+#include "baseprefs.h"
+
+using namespace KGrid2D;
+
+//-----------------------------------------------------------------------------
+FixedCanvasView::FixedCanvasView(QWidget *parent, const char *name)
+ : QCanvasView(parent, name, WNoAutoErase)
+{}
+
+QSize FixedCanvasView::sizeHint() const
+{
+ if ( canvas()==0 ) return QSize();
+ return canvas()->size() + 2 * QSize(frameWidth(), frameWidth());
+}
+
+void FixedCanvasView::adjustSize()
+{
+ setFixedSize(sizeHint());
+}
+
+//-----------------------------------------------------------------------------
+const BaseBoard::DirectionData BaseBoard::DIRECTION_DATA[Nb_Direction] = {
+ { SquareBase::Left, Left },
+ { SquareBase::Right, Right },
+ { SquareBase::Down, Up },
+ { SquareBase::Up, Down }
+};
+
+BaseBoard::BaseBoard(bool graphic, QWidget *parent)
+: FixedCanvasView(parent, "board"),
+ GenericTetris(bfactory->bbi.width, bfactory->bbi.height,
+ bfactory->bbi.withPieces, graphic),
+ state(GameOver), timer(this), sequences(0), main(0), _next(0),
+ _arcade(false)
+{
+ if (graphic) {
+ setVScrollBarMode(AlwaysOff);
+ setHScrollBarMode(AlwaysOff);
+ setFrameStyle( QFrame::Panel | QFrame::Sunken );
+
+ sequences = new SequenceArray;
+ main = new BlockInfo(*sequences);
+ setCanvas(main);
+ if (bfactory->bbi.withPieces)
+ _next = new BlockInfo(*sequences);
+ setBlockInfo(main, _next);
+
+ connect(&timer, SIGNAL(timeout()), SLOT(timeout()));
+
+ Piece::info().loadColors();
+ KZoomMainWindow::addWidget(this);
+ }
+}
+
+void BaseBoard::copy(const GenericTetris &g)
+{
+ GenericTetris::copy(g);
+ state = static_cast<const BaseBoard &>(g).state;
+}
+
+void BaseBoard::settingsChanged()
+{
+ Q_ASSERT( graphic() );
+ Piece::info().loadColors();
+}
+
+void BaseBoard::adjustSize()
+{
+ int size = BasePrefs::blockSize();
+
+ sequences->setBlockSize(size);
+ main->resize(matrix().width() * size, matrix().height() * size);
+ for (uint i=0; i<matrix().width(); i++)
+ for (uint j=0; j<firstClearLine(); j++) {
+ Coord c(i, j);
+ if ( matrix()[c]==0 ) continue;
+ partialMoveBlock(c, QPoint(0, 0));
+ }
+
+ if (_next) {
+ Coord c = Piece::info().maxSize() + Coord(2, 2);
+ _next->resize(c.first * size, c.second * size);
+ _nextPiece->moveCenter();
+ }
+
+ FixedCanvasView::adjustSize();
+}
+
+BaseBoard::~BaseBoard()
+{
+ if ( graphic() ) {
+ setBlockInfo(0, 0); // destruct all sprites before deleting canvas
+ delete _next;
+ delete main;
+ delete sequences;
+ }
+}
+
+void BaseBoard::init(bool arcade)
+{
+ _arcade = arcade;
+ _arcadeStageDone = false;
+}
+
+void BaseBoard::start(const GTInitData &data)
+{
+ Q_ASSERT( graphic() );
+ if ( !_arcadeStageDone || _arcadeStage==bfactory->bbi.nbArcadeStages )
+ _arcadeStage = 0;
+ _arcadeStageDone = false;
+ state = Normal;
+ GenericTetris::start(data); // NB: the timer is started by updateLevel !
+ if (_arcade) arcadePrepare();
+}
+
+void BaseBoard::stop()
+{
+ timer.stop();
+ state = GameOver;
+}
+
+void BaseBoard::pause()
+{
+ Q_ASSERT( graphic() );
+ timer.stop();
+ _oldState = state;
+ state = Paused;
+ showBoard(false);
+}
+
+void BaseBoard::gameOver()
+{
+ stop();
+ emit gameOverSignal();
+}
+
+void BaseBoard::showCanvas(QCanvas *c, bool show)
+{
+ QCanvasItemList l = c->allItems();
+ QCanvasItemList::Iterator it;
+ for (it=l.begin(); it!=l.end(); ++it) {
+ if (show) (*it)->show();
+ else (*it)->hide();
+ }
+ c->update();
+}
+
+void BaseBoard::showBoard(bool show)
+{
+ showCanvas(main, show);
+}
+
+void BaseBoard::unpause()
+{
+ Q_ASSERT( graphic() );
+ showBoard(true);
+ state = _oldState;
+ startTimer();
+}
+
+void BaseBoard::updateRemoved(uint newRemoved)
+{
+ GenericTetris::updateRemoved(newRemoved);
+ emit removedUpdated();
+}
+
+void BaseBoard::updateScore(uint newScore)
+{
+ GenericTetris::updateScore(newScore);
+ emit scoreUpdated();
+}
+
+int BaseBoard::firstColumnBlock(uint col) const
+{
+ for (int j=firstClearLine()-1; j>=0; j--) {
+ Coord c(col, j);
+ if ( matrix()[c]!=0 ) return j;
+ }
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+void BaseBoard::_beforeRemove(bool first)
+{
+ if ( graphic() ) {
+ state = ( beforeRemove(first) ? BeforeRemove : Normal );
+ if ( state==BeforeRemove ) {
+ startTimer();
+ return;
+ }
+ }
+ remove();
+ _afterRemove(true);
+}
+
+void BaseBoard::remove()
+{
+ for (uint j=0; j<firstClearLine(); j++)
+ for (uint i=0; i<matrix().width(); i++) {
+ Coord c(i, j);
+ if ( matrix()[c]==0 || !toBeRemoved(c) ) continue;
+ removeBlock(c);
+ }
+ computeInfos();
+ if ( graphic() ) {
+ main->update();
+ KNotifyClient::event(winId(), "removed", i18n("Blocks removed"));
+ }
+}
+
+bool BaseBoard::doFall(bool doAll, bool first, bool lineByLine)
+{
+ Q_ASSERT( !lineByLine || !doAll );
+
+ if ( !doAll ) {
+ if (first) loop = 0;
+ else loop++;
+ }
+ bool final = (doAll || lineByLine
+ || loop==bfactory->bbi.nbFallStages);
+
+ for (uint i=0; i<matrix().width(); i++) {
+ // compute heights
+ // we must separate this computation since toFall() can depend
+ // directly on the disposition of blocks under the current one
+ // (for e.g. in kfouleggs)
+ // we do not rely on firstClearLine() here since this method is
+ // used in kfouleggs to make gift blocks fall down ...
+ uint h = 0;
+ QMemArray<uint> heights(matrix().height());
+ for (uint j=1; j<matrix().height(); j++) { // first line cannot fall
+ Coord src(i, j);
+ if ( toFall(src) ) h++;
+ heights[j] = h;
+ }
+
+ // do move
+ for (uint j=1; j<matrix().height(); j++) {
+ Coord src(i, j);
+ if( heights[j]==0 || matrix()[src]==0 ) continue;
+ if (lineByLine) final = false;
+ uint k = j - (lineByLine ? 1 : heights[j]);
+ Coord dest(i, k);
+ if ( final || lineByLine ) moveBlock(src, dest);
+ else partialBlockFall(src, dest);
+ }
+ }
+
+ if (final) computeInfos();
+ return final;
+}
+
+void BaseBoard::_afterRemove(bool first)
+{
+ AfterRemoveResult r = afterRemove(!graphic(), first);
+ switch (r) {
+ case Done:
+ state = Normal;
+ _afterAfterRemove();
+ return;
+ case NeedAfterRemove:
+ state = AfterRemove;
+ startTimer();
+ return;
+ case NeedRemoving:
+ _beforeRemove(true);
+ return;
+ }
+}
+
+BaseBoard::AfterRemoveResult BaseBoard::afterRemove(bool doAll, bool first)
+{
+ return (doFall(doAll, first, false) ? Done : NeedAfterRemove);
+}
+
+void BaseBoard::_afterAfterRemove()
+{
+ if ( isArcade() && arcadeDone()>=arcadeTodo() ) {
+ _arcadeStage++;
+ _arcadeStageDone = true;
+ gameOver();
+ return;
+ }
+ if ( !afterAfterRemove() ) gameOver();
+ else if ( graphic() ) startTimer();
+}
+
+bool BaseBoard::timeout()
+{
+ Q_ASSERT( graphic() );
+ if ( state==GameOver ) return true;
+ switch (state) {
+ case BeforeRemove: _beforeRemove(FALSE); break;
+ case AfterRemove: _afterRemove(FALSE); break;
+ default: return false;
+ }
+ main->update();
+ return true;
+}
+
+bool BaseBoard::startTimer()
+{
+ Q_ASSERT( graphic() );
+ if ( state==GameOver ) return true;
+ switch (state) {
+ case BeforeRemove:
+ timer.start(bfactory->bbi.beforeRemoveTime, true);
+ break;
+ case AfterRemove:
+ timer.start(bfactory->bbi.afterRemoveTime, true);
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool BaseBoard::beforeRemove(bool first)
+{
+ if (first) loop = 0;
+ else loop++;
+
+ for (uint j=0; j<firstClearLine(); j++)
+ for (uint i=0; i<matrix().width(); i++) {
+ Coord c(i, j);
+ if ( toBeRemoved(c) ) matrix()[c]->toggleLight();
+ }
+
+ return ( loop!=bfactory->bbi.nbToggles );
+}
+
+
+//-----------------------------------------------------------------------------
+void BaseBoard::partialBlockFall(const Coord &src, const Coord &dest)
+{
+ Q_ASSERT( loop<bfactory->bbi.nbFallStages );
+
+ float c = float(loop+1) / bfactory->bbi.nbFallStages * BasePrefs::blockSize();
+ int xdec = dest.first - src.first;
+ int ydec = src.second - dest.second;
+ QPoint p(int(xdec * c), int(ydec * c));
+ partialMoveBlock(src, p);
+}
+
+uint BaseBoard::findGroup(Square<int> &field, const Coord &c) const
+{
+ uint nb = 0;
+ _findGroup(field, c, nb, false);
+ return nb;
+}
+
+void BaseBoard::setGroup(Square<int> &field, const Coord &c, uint nb) const
+{
+ _findGroup(field, c, nb, true);
+}
+
+void BaseBoard::_findGroup(Square<int> &field, const Coord &c,
+ uint &nb, bool set) const
+{
+ if (!set) nb++;
+ field[c] = (set ? (int)nb : -1);
+ uint value = matrix()[c]->value();
+ CoordList n = matrix().neighbours(c, true, true);
+ for (CoordList::const_iterator i = n.begin(); i!=n.end(); ++i)
+ blockInGroup(field, *i, value, nb, set);
+}
+
+void BaseBoard::blockInGroup(Square<int> &field, const Coord &c, uint value,
+ uint &nb, bool set) const
+{
+ if ( matrix()[c]==0 ) return;
+ if ( matrix()[c]->value()!=value ) return;
+ if ( field[c]!=(set ? -1 : 0) ) return;
+ _findGroup(field, c, nb, set);
+}
+
+QMemArray<uint> BaseBoard::findGroups(Square<int> &field, uint minSize,
+ bool exitAtFirstFound) const
+{
+ field.fill(0);
+ QMemArray<uint> groups;
+ for (uint j=0; j<firstClearLine(); j++)
+ for (uint i=0; i<matrix().width(); i++) {
+ Coord c(i, j);
+ if ( matrix()[c]==0 || matrix()[c]->isGarbage() ) continue;
+ if ( field[c]!=0 ) continue;
+ uint nb = findGroup(field, c);
+ setGroup(field, c, nb);
+ if ( nb>=minSize ) {
+ uint s = groups.size();
+ groups.resize(s+1);
+ groups[s] = nb;
+ if (exitAtFirstFound) return groups;
+ }
+ }
+ return groups;
+}
+
+uint BaseBoard::drawCode(const Coord &c) const
+{
+ uint v = matrix()[c]->value();
+ uint code = 0;
+ for (uint i=0; i<Nb_Direction; i++) {
+ Coord nc = SquareBase::neighbour(c, DIRECTION_DATA[i].neighbour);
+ if ( !matrix().inside(nc) || matrix()[nc]==0
+ || matrix()[nc]->value()!=v ) continue;
+ code |= DIRECTION_DATA[i].direction;
+ }
+ return code;
+}
+
+void BaseBoard::computeNeighbours()
+{
+ for (uint j=0; j<firstClearLine(); j++)
+ for (uint i=0; i<matrix().width(); i++) {
+ Coord c(i, j);
+ if ( matrix()[c]==0 || matrix()[c]->isGarbage() ) continue;
+ matrix()[c]->sprite()->setFrame( drawCode(c) );
+ }
+}
diff --git a/libksirtet/base/board.h b/libksirtet/base/board.h
new file mode 100644
index 00000000..443c6532
--- /dev/null
+++ b/libksirtet/base/board.h
@@ -0,0 +1,134 @@
+#ifndef BASE_BOARD_H
+#define BASE_BOARD_H
+
+#include <qtimer.h>
+#include <qcanvas.h>
+
+#include "gtetris.h"
+
+#include <kdemacros.h>
+
+class SequenceArray;
+class BlockInfo;
+
+//-----------------------------------------------------------------------------
+class KDE_EXPORT FixedCanvasView : public QCanvasView
+{
+ Q_OBJECT
+public:
+ FixedCanvasView(QWidget *parent = 0, const char *name = 0);
+
+ virtual QSize sizeHint() const;
+
+public slots:
+ virtual void adjustSize();
+};
+
+//-----------------------------------------------------------------------------
+class KDE_EXPORT BaseBoard : public FixedCanvasView, public GenericTetris
+{
+ Q_OBJECT
+ public:
+ enum Direction { Left = 1, Right = 2, Up = 4, Down = 8, Nb_Direction = 4 };
+ private:
+ struct DirectionData {
+ KGrid2D::SquareBase::Neighbour neighbour;
+ Direction direction;
+ };
+ static const DirectionData DIRECTION_DATA[Nb_Direction];
+
+ public:
+ BaseBoard(bool graphic, QWidget *parent);
+ virtual ~BaseBoard();
+ void copy(const GenericTetris &);
+
+ void init(bool arcade);
+ virtual void start(const GTInitData &);
+ virtual void pause();
+ virtual void unpause();
+ virtual void stop();
+ bool isGameOver() const { return state==GameOver; }
+ bool isPaused() const { return state==Paused; }
+
+ bool isArcade() const { return _arcade; }
+ uint arcadeStage() const { return _arcadeStage; }
+ bool arcadeStageDone() const { return _arcadeStageDone; }
+ virtual uint arcadeTodo() const { return 0; }
+ virtual uint arcadeDone() const { return 0; }
+
+ virtual void settingsChanged();
+ BlockInfo *next() const { return _next; }
+
+ int firstColumnBlock(uint column) const;
+
+ public slots:
+ virtual void adjustSize();
+
+ protected slots:
+ virtual bool timeout(); // return true if treated
+
+ signals:
+ void updatePieceConfigSignal();
+ void removedUpdated();
+ void scoreUpdated();
+ void gameOverSignal();
+
+ protected:
+ virtual bool beforeRemove(bool first);
+ void _beforeRemove(bool first);
+ enum AfterRemoveResult { Done, NeedAfterRemove, NeedRemoving };
+ virtual AfterRemoveResult afterRemove(bool doAll, bool first);
+ void _afterAfterRemove();
+ virtual bool afterAfterRemove() = 0;
+ virtual bool startTimer(); // return true if treated
+ virtual bool toBeRemoved(const KGrid2D::Coord &) const = 0;
+ virtual void remove();
+ virtual bool toFall(const KGrid2D::Coord &) const = 0;//height>0 when called
+ virtual bool doFall(bool doAll, bool first, bool lineByLine);
+ virtual void gameOver();
+ virtual void arcadePrepare() {}
+
+ uint drawCode(const KGrid2D::Coord &) const;
+ void computeNeighbours();
+ void partialBlockFall(const KGrid2D::Coord &src, const KGrid2D::Coord &dest);
+
+ // return the sizes of the groups (>=minSize)
+ QMemArray<uint> findGroups(KGrid2D::Square<int> &field, uint minSize,
+ bool exitAtFirstFound = false) const;
+ // find group size and put -1 in the corresponding blocks (these blocks
+ // should be 0 at start)
+ uint findGroup(KGrid2D::Square<int> &field, const KGrid2D::Coord &) const;
+ // set the size of the group in the blocks (these blocks should be -1
+ // at start ie you should have called findGroup() before)
+ void setGroup(KGrid2D::Square<int> &field, const KGrid2D::Coord &c,
+ uint nb) const;
+
+ void updateRemoved(uint newRemoved);
+ void updateScore(uint newScore);
+
+ virtual void showBoard(bool show);
+ void showCanvas(QCanvas *c, bool show);
+
+ enum BoardState { GameOver, Normal, Paused,
+ DropDown, BeforeGlue, AfterGlue, BeforeRemove,
+ AfterRemove, AfterGift };
+ BoardState state, _oldState;
+ QTimer timer;
+ SequenceArray *sequences;
+ BlockInfo *main, *_next;
+ uint loop;
+
+ private:
+ bool _arcade, _arcadeStageDone;
+ uint _arcadeStage;
+
+ void _afterRemove(bool first);
+ void updatePieceConfig() { emit updatePieceConfigSignal(); }
+
+ void _findGroup(KGrid2D::Square<int> &field, const KGrid2D::Coord &c,
+ uint &nb, bool set) const;
+ void blockInGroup(KGrid2D::Square<int> &field, const KGrid2D::Coord &c,
+ uint value, uint &nb, bool ser) const;
+};
+
+#endif
diff --git a/libksirtet/base/factory.cpp b/libksirtet/base/factory.cpp
new file mode 100644
index 00000000..55850f0b
--- /dev/null
+++ b/libksirtet/base/factory.cpp
@@ -0,0 +1,52 @@
+#include "factory.h"
+
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kglobal.h>
+#include <klocale.h>
+
+#include "settings.h"
+
+
+BaseFactory *BaseFactory::_self = 0;
+
+BaseFactory::BaseFactory(const MainData &md, const BaseBoardInfo &bi)
+ : mainData(md), bbi(bi)
+{
+ Q_ASSERT( _self==0 );
+ _self = this;
+ _aboutData =
+ new KAboutData(md.appName, md.trName, md.longVersion, md.description,
+ KAboutData::License_GPL,
+ "(c) 1995, Eirik Eng\n(c) 1996-2004, Nicolas Hadacek",
+ 0, md.homepage);
+ _aboutData->addAuthor("Nicolas Hadacek", 0, "hadacek@kde.org");
+ _aboutData->addCredit("Eirik Eng", I18N_NOOP("Core engine"));
+}
+
+void BaseFactory::init(int argc, char **argv)
+{
+ KCmdLineArgs::init(argc, argv, _aboutData);
+ (void)new KApplication;
+ KGlobal::locale()->insertCatalogue("libkdegames");
+ KGlobal::locale()->insertCatalogue("libksirtet");
+}
+
+BaseFactory::~BaseFactory()
+{
+ delete kapp;
+ delete _aboutData;
+ Q_ASSERT(_self);
+ _self = 0;
+}
+
+QWidget *BaseFactory::createAppearanceConfig()
+{
+ return new BaseAppearanceConfig;
+}
+
+QWidget *BaseFactory::createColorConfig()
+{
+ return new ColorConfig;
+}
diff --git a/libksirtet/base/factory.h b/libksirtet/base/factory.h
new file mode 100644
index 00000000..b542205e
--- /dev/null
+++ b/libksirtet/base/factory.h
@@ -0,0 +1,60 @@
+#ifndef BASE_FACTORY_H
+#define BASE_FACTORY_H
+
+#include <qglobal.h>
+
+#include <kdemacros.h>
+
+struct MainData {
+ const char *appName, *trName, *description, *homepage, *removedLabel,
+ *version, *longVersion;
+};
+
+struct BaseBoardInfo {
+ uint width, height;
+ bool withPieces;
+
+ uint beforeRemoveTime, afterRemoveTime;
+ uint nbToggles, nbFallStages;
+
+ uint nbArcadeStages;
+
+ const uint *histogram;
+ uint histogramSize;
+ bool scoreBound;
+};
+
+class BaseBoard;
+class BaseInterface;
+class QWidget;
+class KAboutData;
+
+#define bfactory BaseFactory::self()
+
+class KDE_EXPORT BaseFactory
+{
+ public:
+ BaseFactory(const MainData &, const BaseBoardInfo &);
+ virtual ~BaseFactory();
+ void init(int argc, char **argv);
+
+ static BaseFactory *self() { return _self; }
+
+ const MainData &mainData;
+ const BaseBoardInfo &bbi;
+
+ virtual BaseBoard *createBoard(bool graphic, QWidget *parent) = 0;
+ virtual BaseInterface *createInterface(QWidget *parent) = 0;
+
+ virtual QWidget *createAppearanceConfig();
+ virtual QWidget *createColorConfig();
+ virtual QWidget *createGameConfig() { return 0; }
+
+ protected:
+ KAboutData *_aboutData;
+
+ private:
+ static BaseFactory *_self;
+};
+
+#endif
diff --git a/libksirtet/base/field.cpp b/libksirtet/base/field.cpp
new file mode 100644
index 00000000..53f6220a
--- /dev/null
+++ b/libksirtet/base/field.cpp
@@ -0,0 +1,162 @@
+#include "field.h"
+
+#include <qwhatsthis.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+
+#include <klocale.h>
+#include <kcanvasrootpixmap.h>
+#include <knotifyclient.h>
+#include <kgamelcd.h>
+
+#include "factory.h"
+#include "board.h"
+#include "baseprefs.h"
+
+
+const char *BaseField::BUTTON_TEXTS[NB_BUTTON_TYPE] = {
+ I18N_NOOP("Start"), I18N_NOOP("Resume"), I18N_NOOP("Proceed")
+};
+
+BaseField::BaseField(QWidget *w)
+ : _widget(w), _boardLayout(0), _label(0), _button(0)
+{
+ top = new QGridLayout(w, 3, 5, 10);
+
+ lcds = new QGridLayout(7, 1, 5);
+ top->addLayout(lcds, 1, 0);
+ lcds->setRowStretch(1, 0);
+
+ board = bfactory->createBoard(true, w);
+ _boardRootPixmap = new KCanvasRootPixmap(board);
+ _boardRootPixmap->start();
+ top->addWidget(board, 1, 2);
+}
+
+void BaseField::init(bool AI, bool multiplayer, bool server, bool first,
+ const QString &name)
+{
+ _flags.AI = AI;
+ _flags.multiplayer = multiplayer;
+ _flags.server = server;
+ _flags.first = first;
+ QString text = (AI ? i18n("%1\n(AI player)").arg(name)
+ : (multiplayer ? i18n("%1\n(Human player)").arg(name)
+ : QString::null));
+ if ( first && !server ) text += i18n("\nWaiting for server");
+ setMessage(text, (first && server ? StartButton : NoButton));
+ showScore->resetColor();
+ board->init(false);
+}
+
+void BaseField::setArcade()
+{
+ board->init(true);
+ setMessage(i18n("Stage #1"), StartButton);
+}
+
+bool BaseField::isArcade() const
+{
+ return board->isArcade();
+}
+
+void BaseField::setMessage(const QString &label, ButtonType type)
+{
+ delete _label;
+ _label = 0;
+ delete _button;
+ _button = 0;
+ delete _boardLayout;
+ _boardLayout = 0;
+
+ if ( label.isEmpty() && type==NoButton ) {
+ _widget->setFocus();
+ return;
+ }
+
+ _boardLayout = new QVBoxLayout(board);
+ _boardLayout->addStretch(3);
+ if ( !label.isEmpty() ) {
+ QString str = (isArcade() ? i18n("Arcade game") + '\n'
+ : QString::null) + label;
+ _label = new QLabel(str, board);
+ _label->setAlignment(Qt::AlignCenter);
+ _label->setFrameStyle( QFrame::Panel | QFrame::Sunken );
+ _boardLayout->addWidget(_label, 0, Qt::AlignCenter);
+ _label->show();
+ }
+ _boardLayout->addStretch(1);
+ if ( type!=NoButton ) {
+ _button = new QPushButton(i18n(BUTTON_TEXTS[type]), board);
+ _button->setFocus();
+ const char *slot = (type==ResumeButton ? SLOT(pause())
+ : SLOT(start()));
+ _button->connect(_button, SIGNAL(clicked()),
+ _widget->parent(), slot);
+ _boardLayout->addWidget(_button, 0, Qt::AlignCenter);
+ _button->show();
+ }
+ _boardLayout->addStretch(3);
+}
+
+void BaseField::start(const GTInitData &data)
+{
+ _firstScore = KExtHighscore::firstScore();
+ _lastScore = KExtHighscore::lastScore();
+ hideMessage();
+ board->start(data);
+}
+
+void BaseField::pause(bool pause)
+{
+ if (pause) {
+ board->pause();
+ setMessage(i18n("Game paused"), ResumeButton);
+ } else {
+ board->unpause();
+ hideMessage();
+ }
+}
+
+void BaseField::stop(bool gameover)
+{
+ board->stop();
+ ButtonType button = StartButton;
+ QString msg = (gameover ? i18n("Game over") : QString::null);
+ if ( board->isArcade() && board->arcadeStageDone() ) {
+ if ( board->arcadeStage()==bfactory->bbi.nbArcadeStages )
+ msg = i18n("The End");
+ else {
+ msg = i18n("Stage #%1 done").arg(board->arcadeStage());
+ button = ProceedButton;
+ }
+ }
+ setMessage(msg, button);
+}
+
+void BaseField::gameOver(const KExtHighscore::Score &score, QWidget *parent)
+{
+ KNotifyClient::event(parent->winId(), "game over", i18n("Game Over"));
+ KExtHighscore::submitScore(score, parent);
+}
+
+void BaseField::scoreUpdated()
+{
+ showScore->display( (int)board->score() );
+ if (_flags.multiplayer) return;
+
+ QColor color;
+ if ( _firstScore<currentScore() ) color = Qt::red;
+ else if ( _lastScore<currentScore() ) color = Qt::blue;
+ showScore->setColor(color);
+}
+
+void BaseField::settingsChanged()
+{
+ QColor color = BasePrefs::fadeColor();
+ double s = BasePrefs::fadeIntensity();
+ _boardRootPixmap->setFadeEffect(s, color);
+ board->canvas()->setBackgroundColor(color);
+ board->settingsChanged();
+}
diff --git a/libksirtet/base/field.h b/libksirtet/base/field.h
new file mode 100644
index 00000000..d006a052
--- /dev/null
+++ b/libksirtet/base/field.h
@@ -0,0 +1,66 @@
+#ifndef BASE_FIELD_H
+#define BASE_FIELD_H
+
+#include <kexthighscore.h>
+
+#include <kdemacros.h>
+
+class QVBoxLayout;
+class QGridLayout;
+class KGameLCD;
+class KGameLCDList;
+class BaseBoard;
+class QLabel;
+class QButton;
+class GTInitData;
+class KCanvasRootPixmap;
+
+class KDE_EXPORT BaseField
+{
+ public:
+ BaseField(QWidget *widget);
+ virtual ~BaseField() {}
+
+ virtual KExtHighscore::Score currentScore() const = 0;
+ static void gameOver(const KExtHighscore::Score &, QWidget *parent);
+
+ virtual void setArcade();
+ bool isArcade() const;
+
+ protected:
+ QGridLayout *top, *lcds;
+ KGameLCD *showScore;
+ KGameLCDList *removedList, *scoreList;
+ BaseBoard *board;
+
+ virtual void scoreUpdated();
+ virtual void init(bool AI, bool multiplayer, bool server, bool first,
+ const QString &name);
+ virtual void start(const GTInitData &);
+ virtual void pause(bool pause);
+ virtual void stop(bool gameover);
+ virtual void settingsChanged();
+
+ private:
+ QWidget *_widget;
+ struct Flags {
+ bool AI, multiplayer, server, first;
+ };
+ Flags _flags;
+ uint _arcadeStage;
+ QVBoxLayout *_boardLayout;
+ QLabel *_label;
+ QButton *_button;
+ KCanvasRootPixmap *_boardRootPixmap;
+ KExtHighscore::Score _firstScore, _lastScore;
+
+ enum ButtonType { StartButton = 0, ResumeButton, ProceedButton,
+ NB_BUTTON_TYPE, NoButton = NB_BUTTON_TYPE };
+ static const char *BUTTON_TEXTS[NB_BUTTON_TYPE];
+
+ bool hasButton() const { return _flags.server && _flags.first; }
+ void setMessage(const QString &label, ButtonType);
+ void hideMessage() { setMessage(QString::null, NB_BUTTON_TYPE); }
+};
+
+#endif
diff --git a/libksirtet/base/gtetris.cpp b/libksirtet/base/gtetris.cpp
new file mode 100644
index 00000000..2141f9ef
--- /dev/null
+++ b/libksirtet/base/gtetris.cpp
@@ -0,0 +1,241 @@
+#include "gtetris.h"
+
+#include "piece.h"
+
+
+using namespace KGrid2D;
+
+GenericTetris::GenericTetris(uint width, uint height, bool withPieces,
+ bool graphic)
+ : _nextPiece(0), _currentPiece(0), _score(0), _nbRemoved(0),
+ _nbClearLines(height), _main(0),
+ _graphic(graphic), _matrix(width, height)
+{
+ if (withPieces) {
+ _nextPiece = new Piece;
+ _currentPiece = new Piece;
+ }
+ _matrix.fill(0);
+}
+
+void GenericTetris::copy(const GenericTetris &g)
+{
+ Q_ASSERT(_currentPiece);
+ // copy to non graphic
+ _score = g._score;
+ _level = g._level;
+ _nbRemoved = g._nbRemoved;
+ _nbClearLines = g._nbClearLines;
+ _currentPos = g._currentPos;
+ _nextPiece->copy(g._nextPiece);
+ _currentPiece->copy(g._currentPiece);
+ for (uint i=0; i<_matrix.size(); i++) {
+ Coord c = _matrix.coord(i);
+ delete _matrix[c];
+ if ( g._matrix[c] ) _matrix[c] = new Block(g._matrix[c]->value());
+ else _matrix[c] = 0;
+ }
+}
+
+void GenericTetris::clear()
+{
+ _currentPos = Coord(0, -1);
+ for (uint i=0; i<_matrix.size(); i++) removeBlock(_matrix.coord(i));
+ computeInfos();
+}
+
+GenericTetris::~GenericTetris()
+{
+ // everything should already be done by setBlockInfo(0, 0);
+}
+
+void GenericTetris::setBlockInfo(BlockInfo *main, BlockInfo *next)
+{
+ Q_ASSERT( _graphic );
+ if (main) {
+ _main = main;
+ if (_currentPiece) {
+ Q_ASSERT(next);
+ _nextPiece->setBlockInfo(next);
+ _currentPiece->setBlockInfo(main);
+ }
+ } else { // before destruction
+ clear();
+ delete _currentPiece;
+ delete _nextPiece;
+ }
+}
+
+void GenericTetris::start(const GTInitData &data)
+{
+ Q_ASSERT( _graphic );
+ _random.setSeed(data.seed);
+ _initLevel = data.initLevel;
+ updateScore(0);
+ updateLevel(_initLevel);
+ updateRemoved(0);
+ clear();
+ if (_nextPiece) {
+ _nextPiece->setRandomSequence(&_random);
+ _nextPiece->generateNext();
+ newPiece();
+ }
+}
+
+void GenericTetris::dropDown()
+{
+ uint dropHeight = moveTo(Coord(0, -_currentPos.second));
+ pieceDropped(dropHeight);
+}
+
+void GenericTetris::oneLineDown()
+{
+ if ( moveTo(Coord(0, -1))==0 ) pieceDropped(0);
+}
+
+bool GenericTetris::newPiece()
+{
+ Q_ASSERT(_currentPiece);
+ Coord min = _nextPiece->min();
+ _currentPos.second = _matrix.height() - 1 + min.second;
+ _currentPos.first = (_matrix.width() - _nextPiece->size().first)/2
+ - min.first;
+ if ( !canPosition(_currentPos, _nextPiece)) {
+ _currentPos.second = -1;
+ return false;
+ }
+ _currentPiece->copy(_nextPiece);
+ if (_graphic) {
+ _currentPiece->move(toPoint(_currentPos));
+ _currentPiece->show(true);
+ updatePieceConfig();
+ }
+ _nextPiece->generateNext();
+ if (_graphic) {
+ _nextPiece->moveCenter();
+ _nextPiece->show(true);
+ updateNextPiece();
+ }
+ return true;
+}
+
+bool GenericTetris::canPosition(const Coord &pos, const Piece *piece) const
+{
+ for(uint k=0; k<piece->nbBlocks(); k++) {
+ Coord c(piece->pos(k, pos));
+ if ( !_matrix.inside(c) || _matrix[c]!=0 )
+ return false; // outside or something in the way
+ }
+ return true;
+}
+
+uint GenericTetris::moveTo(const Coord &dec)
+{
+ Q_ASSERT(_currentPiece);
+ Q_ASSERT(dec.first==0 || dec.second==0);
+
+ Coord newPos = _currentPos;
+ Coord d(0, 0);
+ uint n, i;
+
+ if (dec.first) {
+ d.first = (dec.first<0 ? -1 : 1);
+ n = kAbs(dec.first);
+ } else {
+ d.second = (dec.second<0 ? -1 : 1);
+ n = kAbs(dec.second);
+ }
+
+ for (i=0; i<n; i++) {
+ if ( !canPosition(newPos + d, _currentPiece) ) break;
+ newPos = newPos + d;
+ }
+ if ( i!=0 ) { // piece can be moved
+ _currentPos = newPos;
+ if (_graphic) {
+ _currentPiece->move(toPoint(newPos));
+ updatePieceConfig();
+ }
+ }
+ return i;
+}
+
+bool GenericTetris::rotate(bool left)
+{
+ Q_ASSERT(_currentPiece);
+
+ Piece tmp;
+ tmp.copy(_currentPiece);
+ QPoint p(0, 0);
+ tmp.rotate(left, p);
+ if ( canPosition(_currentPos, &tmp) ) {
+ if (_graphic) p = toPoint(_currentPos);
+ _currentPiece->rotate(left, p);
+ if (_graphic) updatePieceConfig();
+ return true;
+ }
+ return false;
+}
+
+void GenericTetris::computeInfos()
+{
+ _nbClearLines = 0;
+ for (uint j=_matrix.height(); j>0; j--) {
+ for (uint i=0; i<_matrix.width(); i++)
+ if ( _matrix[Coord(i, j-1)]!=0 ) return;
+ _nbClearLines++;
+ }
+}
+
+void GenericTetris::setBlock(const Coord &c, Block *b)
+{
+ Q_ASSERT( b && _matrix[c]==0 );
+ _matrix[c] = b;
+ if (_graphic) {
+ QPoint p = toPoint(c);
+ b->sprite()->move(p.x(), p.y());
+ }
+}
+
+void GenericTetris::removeBlock(const Coord &c)
+{
+ delete _matrix[c];
+ _matrix[c] = 0;
+}
+
+void GenericTetris::moveBlock(const Coord &src, const Coord &dest)
+{
+ Q_ASSERT( _matrix[dest]==0 );
+ if ( _matrix[src] ) {
+ setBlock(dest, _matrix[src]);
+ _matrix[src] = 0;
+ }
+}
+
+QPoint GenericTetris::toPoint(const Coord &c) const
+{
+ return _main->toPoint(Coord(c.first, _matrix.height() - 1 - c.second));
+}
+
+void GenericTetris::gluePiece()
+{
+ Q_ASSERT(_currentPiece);
+
+ for(uint k=0; k<_currentPiece->nbBlocks(); k++)
+ setBlock(_currentPiece->pos(k, _currentPos),
+ _currentPiece->takeBlock(k));
+ computeInfos();
+}
+
+void GenericTetris::bumpCurrentPiece(int dec)
+{
+ Q_ASSERT( _graphic && _currentPiece );
+ _currentPiece->move(toPoint(_currentPos) + QPoint(0, dec));
+}
+
+void GenericTetris::partialMoveBlock(const Coord &c, const QPoint &dec)
+{
+ Q_ASSERT( _graphic && _matrix[c]!=0 );
+ QPoint p = toPoint(c) + dec;
+ _matrix[c]->sprite()->move(p.x(), p.y());
+}
diff --git a/libksirtet/base/gtetris.h b/libksirtet/base/gtetris.h
new file mode 100644
index 00000000..48aefb9d
--- /dev/null
+++ b/libksirtet/base/gtetris.h
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** Definition of GenericTetris, a generic class for implementing Tetris.
+**
+** Author : Eirik Eng
+** Created : 940126
+** Modified by Nicolas Hadacek
+**
+** Copyright (C) 1994 by Eirik Eng. All rights reserved.
+**
+**---------------------------------------------------------------------------
+**
+** The GenericTetris class is an abstract class that can be used to implement
+** the well known game of Tetris. It is totally independent of any hardware
+** platform or user interface mechanism. It has no notion of time, so you
+** have to supply the timer ticks (or heartbeat) of the game. Apart from
+** that this is a complete Tetris implementation.
+**
+** In the following it is assumed that the reader is familiar with the game
+** of Tetris.
+**
+** The class operates on a grid of squares (referred to as the "board" below),
+** where each of the different types of pieces in Tetris covers 4 squares. The
+** width and height of the board can be specified in the constructor (default
+** is 10x22). The coordinate (0,0) is at the TOP LEFT corner. The class
+** assumes that it has total control over the board and uses this to optimize
+** drawing of the board. If you need to update parts of the board
+** (e.g. if you are using a window system), use the updateBoard() function.
+**
+** An implementation of the game must subclass from TetrisBoard and must
+** implement these abstract functions:
+**
+** virtual void drawSquare(int x,int y,int value)
+**
+** This function is called when a square needs to be drawn
+** on the Tetris board. A value of 0 means that the square
+** should be erased. Values of 1 to 7 indicate the different
+** types of pieces. (Thus a minimal implementation can
+** draw 0 in one way and 1-7 in a second way). The x and y
+** values are coordinates on the Tetris board (see above).
+**
+** virtual void gameOver()
+**
+** This function is called when it is impossible to put a new
+** piece at the top of the board.
+**
+** To get a working minimal implementation of Tetris the following functions
+** must be called from the user interface:
+**
+** void startGame()
+**
+** Clears the board and starts a new game.
+**
+** void moveLeft()
+** void moveRight()
+** void rotateLeft()
+** void rotateRight()
+**
+** The standard Tetris controls for moving and rotating the
+** falling pieces.
+**
+** void dropDown();
+**
+** Another Tetris control, drops the falling piece and calls the
+** virtual function pieceDropped(), whose default implementation is
+** to create a new block which appears at the top of the board.
+**
+** void oneLineDown();
+**
+** This is where you supply the timer ticks, or heartbeat, of the
+** game. This function moves the current falling piece one line down
+** on the board, or, if that cannot be done, calls the virtual
+** function pieceDropped() (see dropDown() above). The time between
+** each call to this function directly affects the difficulty of the
+** game. If you want to pause the game, simply block calls to the
+** user control functions above and stop calling this function (you
+** might want to call hideBoard() also).
+**
+** And that's it! There are several other public functions you can call
+** and virtual functions you can overload to modify or extend the game.
+**
+** Do whatever you want with this code (i.e. the files gtetris.h,
+** gtetris.cpp, tpiece.h and tpiece.cpp). It is basically a weekend hack
+** and it would bring joy to my heart if anyone in any way would find
+** it useful.
+**
+** Nostalgia, comments and/or praise can be sent to: Eirik.Eng@troll.no
+**
+****************************************************************************/
+
+#ifndef GTETRIS_H
+#define GTETRIS_H
+
+#include <qpoint.h>
+
+#include <krandomsequence.h>
+#include <kgrid2d.h>
+
+#include <kdemacros.h>
+
+class Piece;
+class BlockInfo;
+class Block;
+
+struct GTInitData {
+ int seed;
+ uint initLevel;
+};
+
+class KDE_EXPORT GenericTetris
+{
+ public:
+ GenericTetris(uint width, uint height, bool withPieces, bool graphic);
+ virtual ~GenericTetris();
+ virtual void copy(const GenericTetris &);
+
+ void setBlockInfo(BlockInfo *main, BlockInfo *next);
+ virtual void start(const GTInitData &);
+
+ uint moveLeft(int steps = 1) { return moveTo(KGrid2D::Coord(-steps, 0)); }
+ uint moveRight(int steps = 1) { return moveTo(KGrid2D::Coord(steps, 0)); }
+ bool rotateLeft() { return rotate(true); }
+ bool rotateRight() { return rotate(false); }
+ virtual void oneLineDown();
+ virtual void dropDown();
+
+ KRandomSequence &randomSequence() { return _random; }
+ uint score() const { return _score; }
+ uint level() const { return _level; }
+ uint nbClearLines() const { return _nbClearLines; }
+ uint nbRemoved() const { return _nbRemoved; }
+ bool graphic() const { return _graphic; }
+ uint firstClearLine() const { return _matrix.height() - _nbClearLines; }
+ const KGrid2D::Coord &currentPos() const { return _currentPos; }
+ const Piece *nextPiece() const { return _nextPiece; }
+ const Piece *currentPiece() const { return _currentPiece; }
+ const KGrid2D::Square<Block *> &matrix() const { return _matrix; }
+
+ protected:
+ Piece *_nextPiece, *_currentPiece;
+
+ virtual void pieceDropped(uint /*dropHeight*/) {}
+ virtual bool newPiece(); // return false if cannot place new piece
+ virtual void gluePiece();
+ virtual void computeInfos();
+
+ virtual void updateRemoved(uint newNbRemoved) { _nbRemoved = newNbRemoved;}
+ virtual void updateScore(uint newScore) { _score = newScore; }
+ virtual void updateLevel(uint newLevel) { _level = newLevel; }
+
+ void setBlock(const KGrid2D::Coord &, Block *);
+ virtual void removeBlock(const KGrid2D::Coord &);
+ void moveBlock(const KGrid2D::Coord &src, const KGrid2D::Coord &dest);
+
+ virtual void updateNextPiece() {}
+ virtual void updatePieceConfig() {}
+ void bumpCurrentPiece(int dec);
+ void partialMoveBlock(const KGrid2D::Coord &, const QPoint &dec);
+
+ private:
+ QPoint toPoint(const KGrid2D::Coord &) const;
+ uint moveTo(const KGrid2D::Coord &dec);
+ bool rotate(bool left);
+ void clear();
+ bool canPosition(const KGrid2D::Coord &newPos, const Piece *newPiece) const;
+
+ GenericTetris(const GenericTetris &); // disabled
+
+ uint _score, _level, _nbRemoved;
+ uint _nbClearLines, _initLevel;
+ KGrid2D::Coord _currentPos;
+ BlockInfo *_main;
+ bool _graphic;
+ KGrid2D::Square<Block *> _matrix;
+ KRandomSequence _random;
+};
+
+#endif
diff --git a/libksirtet/base/highscores.cpp b/libksirtet/base/highscores.cpp
new file mode 100644
index 00000000..2b3596d7
--- /dev/null
+++ b/libksirtet/base/highscores.cpp
@@ -0,0 +1,19 @@
+#include "highscores.h"
+
+#include <kurl.h>
+
+#include "factory.h"
+
+
+using namespace KExtHighscore;
+
+BaseHighscores::BaseHighscores()
+{
+ setWWHighscores(KURL( bfactory->mainData.homepage ), bfactory->mainData.version);
+ const BaseBoardInfo &bi = bfactory->bbi;
+ if ( bi.histogramSize!=0 ) {
+ QMemArray<uint> a;
+ a.duplicate(bi.histogram, bi.histogramSize);
+ setScoreHistogram(a, bi.scoreBound ? ScoreBound : ScoreNotBound);
+ }
+}
diff --git a/libksirtet/base/highscores.h b/libksirtet/base/highscores.h
new file mode 100644
index 00000000..b3e7b99e
--- /dev/null
+++ b/libksirtet/base/highscores.h
@@ -0,0 +1,13 @@
+#ifndef BASE_HIGHSCORES_H
+#define BASE_HIGHSCORES_H
+
+#include <kexthighscore.h>
+#include <kdemacros.h>
+
+class KDE_EXPORT BaseHighscores : public KExtHighscore::Manager
+{
+ public:
+ BaseHighscores();
+};
+
+#endif
diff --git a/libksirtet/base/inter.cpp b/libksirtet/base/inter.cpp
new file mode 100644
index 00000000..4f40b63f
--- /dev/null
+++ b/libksirtet/base/inter.cpp
@@ -0,0 +1,15 @@
+#include "inter.h"
+
+#include <kexthighscore.h>
+
+
+void BaseInterface::showHighscores(QWidget *parent)
+{
+ if ( !_isPaused() ) _pause();
+ _showHighscores(parent);
+}
+
+void BaseInterface::_showHighscores(QWidget *parent)
+{
+ KExtHighscore::show(parent);
+}
diff --git a/libksirtet/base/inter.h b/libksirtet/base/inter.h
new file mode 100644
index 00000000..446a77d3
--- /dev/null
+++ b/libksirtet/base/inter.h
@@ -0,0 +1,23 @@
+#ifndef BASE_INTER_H
+#define BASE_INTER_H
+
+class QWidget;
+
+
+class BaseInterface
+{
+public:
+ BaseInterface() {}
+ virtual ~BaseInterface() {}
+
+ virtual void _start() = 0;
+ virtual void _pause() = 0;
+ virtual bool _isPaused() const = 0;
+
+ void showHighscores(QWidget *parent);
+
+protected:
+ virtual void _showHighscores(QWidget *parent);
+};
+
+#endif
diff --git a/libksirtet/base/kzoommainwindow.cpp b/libksirtet/base/kzoommainwindow.cpp
new file mode 100644
index 00000000..115d5175
--- /dev/null
+++ b/libksirtet/base/kzoommainwindow.cpp
@@ -0,0 +1,115 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2004 Nicolas Hadacek (hadacek@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kzoommainwindow.h"
+#include "kzoommainwindow.moc"
+
+#include <kaction.h>
+#include <kstdaction.h>
+#include <kmenubar.h>
+#include <kcmenumngr.h>
+
+KZoomMainWindow::KZoomMainWindow(uint min, uint max, uint step, const char *name)
+ : KMainWindow(0, name), _zoomStep(step), _minZoom(min), _maxZoom(max)
+{
+ installEventFilter(this);
+
+ _zoomInAction = KStdAction::zoomIn(this, SLOT(zoomIn()), actionCollection());
+ _zoomOutAction =
+ KStdAction::zoomOut(this, SLOT(zoomOut()), actionCollection());
+ _menu =
+ KStdAction::showMenubar(this, SLOT(toggleMenubar()), actionCollection());
+}
+
+void KZoomMainWindow::init(const char *popupName)
+{
+ // zoom
+ setZoom(readZoomSetting());
+
+ // menubar
+ _menu->setChecked( menubarVisibleSetting() );
+ toggleMenubar();
+
+ // context popup
+ if (popupName) {
+ QPopupMenu *popup =
+ static_cast<QPopupMenu *>(factory()->container(popupName, this));
+ Q_ASSERT(popup);
+ if (popup) KContextMenuManager::insert(this, popup);
+ }
+}
+
+void KZoomMainWindow::addWidget(QWidget *widget)
+{
+ widget->adjustSize();
+ QWidget *tlw = widget->topLevelWidget();
+ KZoomMainWindow *zm =
+ static_cast<KZoomMainWindow *>(tlw->qt_cast("KZoomMainWindow"));
+ Q_ASSERT(zm);
+ zm->_widgets.append(widget);
+ connect(widget, SIGNAL(destroyed()), zm, SLOT(widgetDestroyed()));
+}
+
+void KZoomMainWindow::widgetDestroyed()
+{
+ _widgets.remove(static_cast<const QWidget *>(sender()));
+}
+
+bool KZoomMainWindow::eventFilter(QObject *o, QEvent *e)
+{
+ if ( e->type()==QEvent::LayoutHint )
+ setFixedSize(minimumSize()); // because K/QMainWindow
+ // does not manage fixed central widget
+ // with hidden menubar...
+ return KMainWindow::eventFilter(o, e);
+}
+
+void KZoomMainWindow::setZoom(uint zoom)
+{
+ _zoom = zoom;
+ writeZoomSetting(_zoom);
+ QPtrListIterator<QWidget> it(_widgets);
+ for (; it.current(); ++it)
+ (*it)->adjustSize();;
+ _zoomOutAction->setEnabled( _zoom>_minZoom );
+ _zoomInAction->setEnabled( _zoom<_maxZoom );
+}
+
+void KZoomMainWindow::zoomIn()
+{
+ setZoom(_zoom + _zoomStep);
+}
+
+void KZoomMainWindow::zoomOut()
+{
+ Q_ASSERT( _zoom>=_zoomStep );
+ setZoom(_zoom - _zoomStep);
+}
+
+void KZoomMainWindow::toggleMenubar()
+{
+ if ( _menu->isChecked() ) menuBar()->show();
+ else menuBar()->hide();
+}
+
+bool KZoomMainWindow::queryExit()
+{
+ writeMenubarVisibleSetting(_menu->isChecked());
+ return KMainWindow::queryExit();
+}
diff --git a/libksirtet/base/kzoommainwindow.h b/libksirtet/base/kzoommainwindow.h
new file mode 100644
index 00000000..14f780fb
--- /dev/null
+++ b/libksirtet/base/kzoommainwindow.h
@@ -0,0 +1,128 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2004 Nicolas Hadacek (hadacek@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KZOOMMAINWINDOW_H
+#define KZOOMMAINWINDOW_H
+
+#include <kmainwindow.h>
+#include <kdemacros.h>
+
+
+class KToggleAction;
+
+/**
+ * KZoomMainWindow is a main window of fixed size. Its size can be
+ * modified with the "zoom in"/"zoom out" actions.
+ *
+ * It manages one or several widgets: their adjustSize() method is
+ * called whenever the zoom level is changed.
+ * The usual implementation for those widget is to redefine adjustSize()
+ * with code like:
+ * /code
+ * setFixedSize(newsize);
+ * /endcode
+ *
+ * This class also has a "show/hide menubar" action and allows the use
+ * of a context popup menu (useful to restore the menubar when hidden).
+ */
+class KDE_EXPORT KZoomMainWindow : public KMainWindow
+{
+ Q_OBJECT
+public:
+ /** Constructor. */
+ KZoomMainWindow(uint minZoom, uint maxZoom, uint zoomStep,
+ const char *name = 0);
+
+ /** Add a widget to be managed i.e. the adjustSize() method of the
+ * widget is called whenever the zoom is changed.
+ * This function assumes that the topLevelWidget() is the KZoomMainWindow.
+ */
+ static void addWidget(QWidget *widget);
+
+ uint zoom() const { return _zoom; }
+
+public slots:
+ void zoomIn();
+ void zoomOut();
+ void toggleMenubar();
+
+protected:
+ /** You need to call this after the createGUI or setupGUI method
+ * is called.
+ * @param popupName is the name of the context popup menu as defined in
+ * the ui.rc file.
+ */
+ void init(const char *popupName = 0);
+
+ virtual void setZoom(uint zoom);
+ virtual bool eventFilter(QObject *o, QEvent *e);
+ virtual bool queryExit();
+
+ /** You need to implement this method since different application
+ * use different setting class names and keys.
+ * Use something like:
+ * /code
+ * Settings::setZoom(zoom);
+ * Settings::writeConfig();
+ * /endcode
+ */
+ virtual void writeZoomSetting(uint zoom) = 0;
+
+ /** Youneed to implement this method since different application
+ * use different setting class names and keys.
+ * Use something like:
+ * /code
+ * return Settings::zoom();
+ * /endcode
+ */
+ virtual uint readZoomSetting() const = 0;
+
+ /** You need to implement this method since different application
+ * use different setting class names and keys.
+ * Use something like:
+ * /code
+ * Settings::setMenubarVisible(visible);
+ * Settings::writeConfig();
+ * /endcode
+ */
+ virtual void writeMenubarVisibleSetting(bool visible) = 0;
+
+ /** You need to implement this method since different application
+ * use different setting class names and keys.
+ * Use something like:
+ * /code
+ * Settings::menubarVisible();
+ * /endcode
+ */
+ virtual bool menubarVisibleSetting() const = 0;
+
+private slots:
+ void widgetDestroyed();
+
+private:
+ uint _zoom, _zoomStep, _minZoom, _maxZoom;
+ QPtrList<QWidget> _widgets;
+ KAction *_zoomInAction, *_zoomOutAction;
+ KToggleAction *_menu;
+
+ class KZoomMainWindowPrivate;
+ KZoomMainWindowPrivate *d;
+};
+
+#endif
diff --git a/libksirtet/base/libksirtet1.kcfg b/libksirtet/base/libksirtet1.kcfg
new file mode 100644
index 00000000..684de57f
--- /dev/null
+++ b/libksirtet/base/libksirtet1.kcfg
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <include>factory.h</include>
+ <include>piece.h</include>
+ <group name="Options">
+ <entry name="BlockSize" type="Int" key="block size">
+ <label>Size of the blocks.</label>
+ <min>4</min>
+ <max>100</max>
+ <default>15</default>
+ </entry>
+ <entry name="FadeIntensity" type="Double" key="fade intensity">
+ <label>Fade intensity.</label>
+ <default>1.0</default>
+ </entry>
+ <entry name="FadeColor" type="Color" key="fade color">
+ <label>Fade color.</label>
+ <default>black</default>
+ </entry>
+ <entry name="AnimationsEnabled" type="Bool" key="enable animations">
+ <label>Animations enabled.</label>
+ <default>true</default>
+ </entry>
+ <entry name="MenubarVisible" type="Bool" key="menubar visible">
+ <label>Menubar visible.</label>
+ <default>true</default>
+ </entry>
+ <entry name="Color$(Number)" type="Color" key="color #$(Number)">
+ <parameter name="Number" type="Int" max="10"/>
+ <label>Block colors.</label>
+<!-- TODO: max, default is dynamic -->
+ <default code="true">Piece::info().defaultColor($(Number))</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/libksirtet/base/main.cpp b/libksirtet/base/main.cpp
new file mode 100644
index 00000000..4b5a4160
--- /dev/null
+++ b/libksirtet/base/main.cpp
@@ -0,0 +1,131 @@
+#include "main.h"
+#include "main.moc"
+
+#include <kaction.h>
+#include <kstdaction.h>
+#include <kmenubar.h>
+#include <kstdgameaction.h>
+#include <kcmenumngr.h>
+#include <kkeydialog.h>
+#include <klocale.h>
+#include <knotifyclient.h>
+#include <knotifydialog.h>
+#include <kexthighscore.h>
+#include <kconfigdialog.h>
+
+#include "inter.h"
+#include "factory.h"
+#include "settings.h"
+#include "baseprefs.h"
+
+BaseMainWindow::BaseMainWindow()
+ : KZoomMainWindow(4, 100, 1, "main_window")
+{
+ KNotifyClient::startDaemon();
+
+ // File & Popup
+ KStdGameAction::gameNew(this, SLOT(start()), actionCollection());
+ _pause = KStdGameAction::pause(this, SLOT(pause()), actionCollection());
+ _pause->setEnabled(false);
+ KStdGameAction::highscores(this, SLOT(showHighscores()),
+ actionCollection());
+ KStdGameAction::quit(qApp, SLOT(quit()), actionCollection());
+
+ // Settings
+ KStdAction::preferences(this, SLOT(configureSettings()),
+ actionCollection());
+ KStdAction::keyBindings(this, SLOT(configureKeys()), actionCollection());
+ KStdAction::configureNotifications(this, SLOT(configureNotifications()),
+ actionCollection());
+ KStdGameAction::configureHighscores(this, SLOT(configureHighscores()),
+ actionCollection());
+
+ _inter = bfactory->createInterface(this);
+}
+
+void BaseMainWindow::buildGUI(QWidget *widget)
+{
+ createGUI();
+ setCentralWidget(widget);
+ init("popup");
+}
+
+BaseMainWindow::~BaseMainWindow()
+{
+ delete _inter;
+}
+
+void BaseMainWindow::showHighscores()
+{
+ _inter->showHighscores(this);
+}
+
+void BaseMainWindow::start()
+{
+ _inter->_start();
+}
+
+void BaseMainWindow::pause()
+{
+ _inter->_pause();
+}
+
+void BaseMainWindow::configureHighscores()
+{
+ KExtHighscore::configure(this);
+}
+
+void BaseMainWindow::configureSettings()
+{
+ if ( !_inter->_isPaused() ) _inter->_pause();
+ if ( KConfigDialog::showDialog("settings") ) return;
+
+ KConfigDialog *dialog = new KConfigDialog(this, "settings", BasePrefs::self() );
+ QWidget *w = bfactory->createGameConfig();
+ if (w) dialog->addPage(w, i18n("Game"), "package_system");
+ w = bfactory->createAppearanceConfig();
+ if (w) dialog->addPage(w, i18n("Appearance"), "style");
+ w = bfactory->createColorConfig();
+ if (w) dialog->addPage(w, i18n("Colors"), "colorize");
+// dialog->addPage(new BackgroundConfigWidget, i18n("Background"), "background");
+ addConfig(dialog);
+ connect(dialog, SIGNAL(settingsChanged()), SIGNAL(settingsChanged()));
+ dialog->show();
+}
+
+void BaseMainWindow::configureKeys()
+{
+ KKeyDialog d(true, this);
+ addKeys(d);
+ d.insert(actionCollection());
+ d.configure(false);
+ actionCollection()->writeShortcutSettings();
+ saveKeys();
+}
+
+void BaseMainWindow::configureNotifications()
+{
+ KNotifyDialog::configure(this);
+}
+
+void BaseMainWindow::writeZoomSetting(uint zoom)
+{
+ BasePrefs::setBlockSize(zoom);
+ BasePrefs::writeConfig();
+}
+
+uint BaseMainWindow::readZoomSetting() const
+{
+ return BasePrefs::blockSize();
+}
+
+void BaseMainWindow::writeMenubarVisibleSetting(bool visible)
+{
+ BasePrefs::setMenubarVisible(visible);
+ BasePrefs::writeConfig();
+}
+
+bool BaseMainWindow::menubarVisibleSetting() const
+{
+ return BasePrefs::menubarVisible();
+}
diff --git a/libksirtet/base/main.h b/libksirtet/base/main.h
new file mode 100644
index 00000000..d2e108aa
--- /dev/null
+++ b/libksirtet/base/main.h
@@ -0,0 +1,49 @@
+#ifndef BASE_MAIN_H
+#define BASE_MAIN_H
+
+#include "kzoommainwindow.h"
+
+#include <kdemacros.h>
+
+class BaseInterface;
+class KToggleAction;
+class KKeyDialog;
+class KConfigDialog;
+
+class KDE_EXPORT BaseMainWindow : public KZoomMainWindow
+{
+ Q_OBJECT
+public:
+ BaseMainWindow();
+ virtual ~BaseMainWindow();
+
+signals:
+ void settingsChanged();
+
+private slots:
+ void start();
+ void pause();
+ void showHighscores();
+ void configureSettings();
+ void configureKeys();
+ void configureHighscores();
+ void configureNotifications();
+
+protected:
+ BaseInterface *_inter;
+
+ void buildGUI(QWidget *widget);
+ virtual void addConfig(KConfigDialog *) {}
+ virtual void addKeys(KKeyDialog &) {}
+ virtual void saveKeys() {}
+
+ virtual void writeZoomSetting(uint zoom);
+ virtual uint readZoomSetting() const;
+ virtual void writeMenubarVisibleSetting(bool visible);
+ virtual bool menubarVisibleSetting() const;
+
+private:
+ KToggleAction *_pause;
+};
+
+#endif
diff --git a/libksirtet/base/piece.cpp b/libksirtet/base/piece.cpp
new file mode 100644
index 00000000..25aed934
--- /dev/null
+++ b/libksirtet/base/piece.cpp
@@ -0,0 +1,274 @@
+#include "piece.h"
+
+#include <kglobal.h>
+#include <krandomsequence.h>
+
+#include "baseprefs.h"
+
+using namespace KGrid2D;
+
+QPoint operator *(const Coord &c, int i)
+{
+ return QPoint(c.first * i, c.second * i);
+}
+
+//-----------------------------------------------------------------------------
+GPieceInfo::GPieceInfo()
+{
+ Piece::setPieceInfo(this);
+}
+
+QPixmap *GPieceInfo::pixmap(uint blockSize, uint blockType, uint blockMode,
+ bool lighted) const
+{
+ QPixmap *pixmap = new QPixmap(blockSize, blockSize);
+ draw(pixmap, blockType, blockMode, lighted);
+ setMask(pixmap, blockMode);
+ return pixmap;
+}
+
+Coord GPieceInfo::maxSize() const
+{
+ Coord min, max;
+ Coord size(0, 0);
+ for (uint n=0; n<nbForms(); n++) {
+ min = max = Coord(i(n, 0)[0], j(n, 0)[0]);
+ for (uint k=0; k<nbBlocks(); k++) {
+ Coord tmp = Coord(i(n, 0)[k], j(n, 0)[k]);
+ max = maximum(max, tmp);
+ min = minimum(min, tmp);
+ }
+ size = maximum(size, max - min);
+ }
+ return size;
+}
+
+uint GPieceInfo::generateType(KRandomSequence *random) const
+{
+ return random->getLong( nbTypes() );
+}
+
+uint GPieceInfo::generateGarbageBlockType(KRandomSequence *random) const
+{
+ return nbNormalBlockTypes() + random->getLong( nbGarbageBlockTypes() );
+}
+
+void GPieceInfo::loadColors()
+{
+ _colors.resize(nbColors());
+ for (uint i=0; i<_colors.size(); i++)
+ _colors[i] = BasePrefs::color(i);
+}
+
+
+//-----------------------------------------------------------------------------
+SequenceArray::SequenceArray()
+: _size(0)
+{
+ const GPieceInfo &pinfo = Piece::info();
+ fill(0, pinfo.nbNormalBlockTypes() + pinfo.nbGarbageBlockTypes());
+}
+
+void SequenceArray::setBlockSize(uint bsize)
+{
+ _size = bsize;
+ const GPieceInfo &pinfo = Piece::info();
+ QPtrList<QPixmap> pixmaps;
+ pixmaps.setAutoDelete(TRUE);
+ QPtrList<QPoint> points;
+ points.setAutoDelete(TRUE);
+ uint nm = pinfo.nbBlockModes();
+ for (uint i=0; i<size(); i++) {
+ for (uint k=0; k<2; k++)
+ for (uint j=0; j<nm; j++) {
+ QPoint *po = new QPoint(0, 0);
+ QPixmap *pi = pinfo.pixmap(bsize, i, j, k==1);
+ if ( at(i) ) {
+ at(i)->setImage(k*nm + j, new QCanvasPixmap(*pi, *po));
+ delete po;
+ delete pi;
+ } else {
+ points.append(po);
+ pixmaps.append(pi);
+ }
+ }
+ if ( at(i)==0 ) {
+ at(i) = new QCanvasPixmapArray(pixmaps, points);
+ pixmaps.clear();
+ points.clear();
+ }
+ }
+}
+
+SequenceArray::~SequenceArray()
+{
+ for (uint i=0; i<size(); i++) delete at(i);
+}
+
+//-----------------------------------------------------------------------------
+BlockInfo::BlockInfo(const SequenceArray &s)
+: _sequences(s)
+{}
+
+QPoint BlockInfo::toPoint(const Coord &pos) const
+{
+ return pos * _sequences.blockSize();
+}
+
+//-----------------------------------------------------------------------------
+Block::Block(uint value)
+: _value(value), _sprite(0)
+{}
+
+Block::~Block()
+{
+ delete _sprite;
+}
+
+void Block::setValue(uint value, BlockInfo *binfo)
+{
+ _value = value;
+ if (binfo) {
+ QCanvasPixmapArray *seq = binfo->sequences()[value];
+ if (_sprite) _sprite->setSequence(seq);
+ else {
+ _sprite = new QCanvasSprite(seq, binfo);
+ _sprite->setZ(0);
+ }
+ }
+}
+
+void Block::toggleLight()
+{
+ const GPieceInfo &pinfo = Piece::info();
+ uint f = _sprite->frame() + pinfo.nbBlockModes()
+ * (_sprite->frame()>=(int)pinfo.nbBlockModes() ? -1 : 1);
+ _sprite->setFrame(f);
+}
+
+bool Block::isGarbage() const
+{
+ return Piece::info().isGarbage(_value);
+}
+
+
+//-----------------------------------------------------------------------------
+GPieceInfo *Piece::_info = 0;
+
+Piece::Piece()
+ : _binfo(0), _i(0), _j(0)
+{
+ _blocks.setAutoDelete(true);
+}
+
+void Piece::rotate(bool left, const QPoint &p)
+{
+ if (left) {
+ if ( _rotation==0 ) _rotation = 3;
+ else _rotation--;
+ } else {
+ if ( _rotation==3 ) _rotation = 0;
+ else _rotation++;
+ }
+
+ uint form = _info->form(_type);
+ _i = _info->i(form, _rotation);
+ _j = _info->j(form, _rotation);
+ if (_binfo) move(p);
+}
+
+Coord Piece::min() const
+{
+ if ( _i==0 || _j==0 ) return Coord(0, 0);
+ Coord min = coord(0);
+ for(uint k=1; k<_info->nbBlocks(); k++)
+ min = minimum(min, coord(k));
+ return min;
+}
+
+Coord Piece::max() const
+{
+ if ( _i==0 || _j==0 ) return Coord(0, 0);
+ Coord max = coord(0);
+ for(uint k=1; k<_info->nbBlocks(); k++)
+ max = maximum(max, coord(k));
+ return max;
+}
+
+void Piece::copy(const Piece *p)
+{
+ if ( p->_blocks.size()!=0 ) {
+ _blocks.resize(p->_blocks.size());
+ for (uint k=0; k<_blocks.size(); k++) {
+ if ( _blocks[k]==0 ) _blocks.insert(k, new Block);
+ _blocks[k]->setValue(p->_blocks[k]->value(), _binfo);
+ }
+ }
+ _type = p->_type;
+ _random = p->_random;
+ _rotation = p->_rotation;
+ _i = p->_i;
+ _j = p->_j;
+}
+
+void Piece::generateNext(int type)
+{
+ if ( _blocks.size()==0 ) {
+ _blocks.resize(_info->nbBlocks());
+ for (uint k=0; k<_blocks.size(); k++) _blocks.insert(k, new Block);
+ }
+ _type = (type==-1 ? _info->generateType(_random) : (uint)type );
+ _rotation = 0;
+
+ uint form = _info->form(_type);
+ _i = _info->i(form, _rotation);
+ _j = _info->j(form, _rotation);
+
+ for (uint k=0; k<_blocks.size(); k++)
+ _blocks[k]->setValue(_info->value(_type, k), _binfo);
+}
+
+void Piece::moveCenter()
+{
+ uint s = _binfo->sequences().blockSize();
+ QPoint p = QPoint(_binfo->width(), _binfo->height()) - size() * s;
+ move(p/2 - min() * s);
+}
+
+Coord Piece::pos(uint k, const Coord &pos) const
+{
+ return Coord(pos.first + coord(k).first, pos.second - coord(k).second);
+}
+
+void Piece::move(const QPoint &p)
+{
+ for (uint k=0; k<_blocks.size(); k++) moveBlock(k, p);
+}
+
+void Piece::moveBlock(uint k, const QPoint &p)
+{
+ QPoint po = p + _binfo->toPoint(coord(k));
+ _blocks[k]->sprite()->move(po.x(), po.y());
+}
+
+Block *Piece::garbageBlock() const
+{
+ Block *b = new Block;
+ b->setValue(_info->generateGarbageBlockType(_random), _binfo);
+ return b;
+}
+
+Block *Piece::takeBlock(uint k)
+{
+ Block *b = _blocks.take(k);
+ _blocks.insert(k, new Block);
+ return b;
+}
+
+void Piece::show(bool show)
+{
+ for (uint k=0; k<_blocks.size(); k++) {
+ if (show) _blocks[k]->sprite()->show();
+ else _blocks[k]->sprite()->hide();
+ }
+}
diff --git a/libksirtet/base/piece.h b/libksirtet/base/piece.h
new file mode 100644
index 00000000..4c0486a8
--- /dev/null
+++ b/libksirtet/base/piece.h
@@ -0,0 +1,155 @@
+#ifndef BASE_PIECE_H
+#define BASE_PIECE_H
+
+#include <qcanvas.h>
+#include <qptrvector.h>
+
+#include <kgrid2d.h>
+
+
+class KRandomSequence;
+
+//-----------------------------------------------------------------------------
+class GPieceInfo
+{
+ public:
+ GPieceInfo();
+ virtual ~GPieceInfo() {}
+
+ virtual uint nbBlocks() const = 0; // nb of blocks in a piece
+ virtual uint nbTypes() const = 0; // nb of combin. of types in piece
+ virtual uint nbForms() const = 0; // nb of geometrical form of piece
+
+ virtual const int *i(uint form, uint rotation) const = 0;
+ virtual const int *j(uint form, uint rotation) const = 0;
+ virtual uint value(uint type, uint n) const = 0;
+ virtual uint form(uint type) const = 0;
+ virtual uint nbConfigurations(uint type) const = 0;
+ uint generateType(KRandomSequence *) const;
+
+ KGrid2D::Coord maxSize() const;
+
+ QPixmap *pixmap(uint blockSize, uint blockType, uint blockMode,
+ bool lighted) const;
+
+ virtual uint nbNormalBlockTypes() const = 0;
+ virtual uint nbGarbageBlockTypes() const = 0;
+ virtual uint nbBlockModes() const = 0; // nb of modes per block
+ bool isGarbage(uint type) const { return type>=nbNormalBlockTypes(); }
+ uint generateGarbageBlockType(KRandomSequence *) const;
+
+ virtual uint nbColors() const = 0;
+ virtual QString colorLabel(uint i) const = 0;
+ QCString colorKey(uint i) const;
+ virtual QColor defaultColor(uint i) const = 0;
+ void loadColors();
+
+ protected:
+ QColor color(uint i) const { return _colors[i]; }
+
+ virtual void draw(QPixmap *, uint blockType, uint blockMode,
+ bool lighted) const = 0;
+ virtual void setMask(QPixmap *, uint /*blockMode*/) const {}
+
+ private:
+ QValueVector<QColor> _colors;
+};
+
+class SequenceArray : public QMemArray<QCanvasPixmapArray *>
+{
+ public:
+ SequenceArray();
+ ~SequenceArray();
+
+ void setBlockSize(uint size);
+ uint blockSize() const { return _size; }
+
+ private:
+ uint _size;
+};
+
+//-----------------------------------------------------------------------------
+class BlockInfo : public QCanvas
+{
+ public:
+ BlockInfo(const SequenceArray &);
+ const SequenceArray &sequences() const { return _sequences; }
+
+ QPoint toPoint(const KGrid2D::Coord &) const;
+
+ private:
+ const SequenceArray &_sequences;
+};
+
+//-----------------------------------------------------------------------------
+class Block
+{
+ public:
+ Block(uint value = 0);
+ ~Block();
+
+ void setValue(uint, BlockInfo *);
+ uint value() const { return _value; }
+ bool isGarbage() const;
+ void toggleLight();
+ QCanvasSprite *sprite() const { return _sprite; }
+
+ private:
+ uint _value;
+ QCanvasSprite *_sprite;
+
+ Block(const Block &); // disabled
+ Block &operator =(const Block &); // disabled
+};
+
+//-----------------------------------------------------------------------------
+class Piece
+{
+ public:
+ Piece();
+
+ void copy(const Piece *);
+ void setBlockInfo(BlockInfo *bi) { _binfo = bi; }
+ static void setPieceInfo(GPieceInfo *pi) { _info = pi; }
+ static GPieceInfo &info() { return *_info; }
+
+ uint type() const { return _type; }
+ uint nbBlocks() const { return _blocks.size(); }
+ uint nbConfigurations() const { return _info->nbConfigurations(_type); }
+
+ int value(uint k) const { return _blocks[k]->value(); }
+ KGrid2D::Coord pos(uint k, const KGrid2D::Coord &) const;
+
+ KGrid2D::Coord min() const;
+ KGrid2D::Coord max() const;
+ KGrid2D::Coord size() const { return max() - min() + KGrid2D::Coord(1, 1); }
+
+ void generateNext(int type = -1);
+ void rotate(bool left, const QPoint &);
+ void move(const QPoint &);
+ void moveCenter();
+ void show(bool show);
+
+ void setRandomSequence(KRandomSequence *random) { _random = random; }
+
+ Block *garbageBlock() const;
+ Block *takeBlock(uint k);
+
+ private:
+ QPtrVector<Block> _blocks;
+ uint _type;
+ KRandomSequence *_random;
+ static GPieceInfo *_info;
+ BlockInfo *_binfo;
+ uint _rotation;
+ int const *_i;
+ int const *_j;
+
+ Piece(const Piece &); // disabled
+ Piece &operator =(const Piece &); // disabled
+
+ KGrid2D::Coord coord(uint k) const { return KGrid2D::Coord(_i[k], _j[k]); }
+ void moveBlock(uint k, const QPoint &);
+};
+
+#endif
diff --git a/libksirtet/base/settings.cpp b/libksirtet/base/settings.cpp
new file mode 100644
index 00000000..1ea3b16a
--- /dev/null
+++ b/libksirtet/base/settings.cpp
@@ -0,0 +1,84 @@
+#include "settings.h"
+#include "settings.moc"
+
+#include <qlabel.h>
+#include <qhbox.h>
+#include <qlayout.h>
+#include <qcheckbox.h>
+#include <qhgroupbox.h>
+
+#include <klocale.h>
+#include <knuminput.h>
+#include <kcolorbutton.h>
+#include <kapplication.h>
+#include <kdialogbase.h>
+
+#include "piece.h"
+#include "factory.h"
+
+
+//-----------------------------------------------------------------------------
+BaseAppearanceConfig::BaseAppearanceConfig()
+ : QWidget(0, "appearance_config")
+{
+ QVBoxLayout *top = new QVBoxLayout(this);
+
+ // upper part
+ _main = new QWidget(this);
+ top->addWidget(_main);
+ _grid = new QGridLayout(_main, 3, 2, 0, KDialog::spacingHint());
+ _grid->setColStretch(1, 1);
+
+ QCheckBox *chb =
+ new QCheckBox(i18n("Enable animations"), _main, "kcfg_AnimationsEnabled");
+ _grid->addMultiCellWidget(chb, 2, 2, 0, 1);
+
+ top->addSpacing(KDialog::spacingHint());
+
+ // lower part
+ QHGroupBox *gbox = new QHGroupBox(i18n("Background"), this);
+ top->addWidget(gbox);
+ QWidget *widget = new QWidget(gbox);
+ QGridLayout *grid =
+ new QGridLayout(widget, 2, 3, 0, KDialog::spacingHint());
+ grid->setColStretch(2, 1);
+ QLabel *label = new QLabel(i18n("Color:"), widget);
+ grid->addWidget(label, 0, 0);
+ KColorButton *cob = new KColorButton(widget, "kcfg_FadeColor");
+ cob->setFixedWidth(100);
+ grid->addWidget(cob, 0, 1);
+ label = new QLabel(i18n("Opacity:"), widget);
+ grid->addWidget(label, 1, 0);
+ KDoubleNumInput *dn = new KDoubleNumInput(widget, "kcfg_FadeIntensity");
+ dn->setRange(0.0, 1.0, 0.01);
+ grid->addMultiCellWidget(dn, 1, 1, 1, 2);
+
+ top->addStretch(1);
+}
+
+//-----------------------------------------------------------------------------
+ColorConfig::ColorConfig()
+ : QWidget(0, "color_config")
+{
+ const GPieceInfo &info = Piece::info();
+ QVBoxLayout *top = new QVBoxLayout(this);
+ uint nb = info.nbColors();
+ QGridLayout *grid = new QGridLayout(top, nb+1, 3, KDialog::spacingHint());
+ grid->setColStretch(2, 1);
+ for (uint i=0; i<nb; i++) {
+ QLabel *label = new QLabel(info.colorLabel(i), this);
+ grid->addWidget(label, i, 0);
+ KColorButton *cob = new KColorButton(this, colorKey(i));
+ cob->setFixedWidth(100);
+ grid->addWidget(cob, i, 1);
+ }
+ grid->setRowStretch(nb, 1);
+}
+
+QCString ColorConfig::colorKey(uint i)
+{
+ QCString s;
+ s.setNum(i);
+ return "kcfg_Color" + s;
+}
+
diff --git a/libksirtet/base/settings.h b/libksirtet/base/settings.h
new file mode 100644
index 00000000..c64bfd5b
--- /dev/null
+++ b/libksirtet/base/settings.h
@@ -0,0 +1,33 @@
+#ifndef BASE_SETTINGS_H
+#define BASE_SETTINGS_H
+
+#include <qwidget.h>
+#include <kconfig.h>
+
+class QGridLayout;
+
+
+//-----------------------------------------------------------------------------
+class BaseAppearanceConfig : public QWidget
+{
+ Q_OBJECT
+public:
+ BaseAppearanceConfig();
+
+protected:
+ QWidget *_main;
+ QGridLayout *_grid;
+};
+
+//-----------------------------------------------------------------------------
+class ColorConfig : public QWidget
+{
+ Q_OBJECT
+public:
+ ColorConfig();
+
+private:
+ static QCString colorKey(uint i);
+};
+
+#endif
diff --git a/libksirtet/common/Makefile.am b/libksirtet/common/Makefile.am
new file mode 100644
index 00000000..1c11265d
--- /dev/null
+++ b/libksirtet/common/Makefile.am
@@ -0,0 +1,28 @@
+INCLUDES = -I$(top_builddir)/libksirtet -I$(top_srcdir)/libksirtet -I$(srcdir)/../base -I$(top_srcdir)/libkdegames/highscore -I$(top_srcdir)/libkdegames $(all_includes)
+
+# Don't compile with hidden symbols since we are a library.
+if disable_VISIBILITY
+KDE_CXXFLAGS = -fvisibility=default
+endif
+
+noinst_LTLIBRARIES = libksirtetcommon.la
+libksirtetcommon_la_LDFLAGS = $(all_libraries) -no-undefined
+libksirtetcommon_la_DEPENDENCIES = $(LIB_KDEGAMES_DEP) $(top_builddir)/libksirtet/lib/libksirtetmultiplayers.la $(top_builddir)/libksirtet/base/libksirtetbase.la
+libksirtetcommon_la_LIBADD = $(LIB_KDEGAMES) $(top_builddir)/libksirtet/lib/libksirtetmultiplayers.la $(top_builddir)/libksirtet/base/libksirtetbase.la
+
+noinst_HEADERS = types.h factory.h misc_ui.h highscores.h \
+ board.h ai.h field.h settings.h inter.h main.h
+libksirtetcommon_la_SOURCES = types.cpp factory.cpp misc_ui.cpp \
+ highscores.cpp \
+ board.cpp ai.cpp field.cpp settings.cpp \
+ inter.cpp main.cpp commonprefs.kcfgc
+METASOURCES = misc_ui.moc board.moc ai.moc field.moc \
+ settings.moc inter.moc main.moc
+
+ai.lo: ../base/baseprefs.h
+board.lo: ../base/baseprefs.h
+commonprefs.lo: ../base/baseprefs.h
+field.lo: ../base/baseprefs.h
+inter.lo: ../base/baseprefs.h
+misc_ui.lo: ../base/baseprefs.h
+
diff --git a/libksirtet/common/README b/libksirtet/common/README
new file mode 100644
index 00000000..03ffa387
--- /dev/null
+++ b/libksirtet/common/README
@@ -0,0 +1 @@
+This directory contains code shared between ksirtet and kfouleggs.
diff --git a/libksirtet/common/ai.cpp b/libksirtet/common/ai.cpp
new file mode 100644
index 00000000..bc1c6722
--- /dev/null
+++ b/libksirtet/common/ai.cpp
@@ -0,0 +1,356 @@
+#include "ai.h"
+#include "ai.moc"
+
+#include <assert.h>
+
+#include <qlabel.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qlayout.h>
+#include <qgrid.h>
+#include <qwhatsthis.h>
+
+#include <klocale.h>
+#include <kconfig.h>
+#include <kapplication.h>
+
+#include "commonprefs.h"
+#include "board.h"
+#include "base/piece.h"
+#include "base/factory.h"
+
+
+//-----------------------------------------------------------------------------
+AIPiece::AIPiece()
+ : _current(0)
+{}
+
+AIPiece::~AIPiece()
+{
+ delete _current;
+}
+
+void AIPiece::init(const Piece *piece, Board *b)
+{
+ _piece = piece;
+ _board = b;
+ if ( _current==0 ) _current = new Piece;
+ reset();
+}
+
+void AIPiece::reset()
+{
+ curPos = 0;
+ curRot = 0;
+ if (_piece) _current->copy(_piece);
+// else _current->generateNext(0);
+ nbRot = _current->nbConfigurations() - 1;
+ nbPos = _board->matrix().width() - _current->size().first + 1;
+}
+
+bool AIPiece::increment()
+{
+ curPos++;
+ if ( curPos==nbPos ) {
+ if ( curRot==nbRot ) {
+// if ( _piece || _current->type()==Piece::info().nbTypes() ) {
+ reset();
+ return false;
+// }
+// _current->generateNext(_current->type()+1);
+// nbRot = _current->nbConfigurations() - 1;
+// curRot = 0;
+ }
+ _current->rotate(true, QPoint(0, 0));
+ nbPos = _board->matrix().width() - _current->size().first + 1;
+ curRot++;
+ curPos = 0;
+ }
+ return true;
+}
+
+bool AIPiece::place()
+{
+ if ( curRot==3 ) {
+ if ( !_board->rotateRight() ) return false;
+ } else for (uint i=0; i<curRot; i++)
+ if ( !_board->rotateLeft() ) return false;
+ curDec = curPos - _board->currentPos().first - _current->min().first;
+ if ( curDec!=0 && _board->moveRight(curDec)!=(uint)kAbs(curDec) )
+ return false;
+ _board->dropDown();
+ return !_board->isGameOver();
+}
+
+//-----------------------------------------------------------------------------
+const AI::Data AI::LastData = { 0, 0, 0, false, 0 };
+
+AI::AI(uint tTime, uint oTime, const Data *DATA)
+ : timer(this), thinkTime(tTime), orderTime(oTime), stopped(false),
+ board(0)
+{
+ connect(&timer, SIGNAL(timeout()), SLOT(timeout()));
+
+ for (uint i=0; DATA[i].name; i++) {
+ Element element;
+ element.coefficient = 0.;
+ element.trigger = 0;
+ element.data = &DATA[i];
+ _elements.append(element);
+ }
+ settingsChanged();
+}
+
+void AI::resizePieces(uint size)
+{
+ uint oldSize = pieces.size();
+ for (uint i=size; i<oldSize; i++) delete pieces[i];
+ pieces.resize(size);
+ for (uint i=oldSize; i<size; i++) pieces[i] = new AIPiece;
+}
+
+AI::~AI()
+{
+ delete board;
+ resizePieces(0);
+}
+
+void AI::initThink()
+{
+ board->copy(*main);
+}
+
+void AI::launch(Board *m)
+{
+ main = m;
+ if ( board==0 )
+ board = static_cast<Board *>(bfactory->createBoard(false, 0));
+
+ pieces[0]->init(main->currentPiece(), board); // current
+ if ( pieces.size()>=2 ) pieces[1]->init(main->nextPiece(), board); // next
+
+ state = Thinking;
+ hasBestPoints = false;
+ startTimer();
+}
+
+void AI::stop()
+{
+ timer.stop();
+ stopped = true;
+}
+
+void AI::start()
+{
+ if (stopped) {
+ startTimer();
+ stopped = false;
+ }
+}
+
+void AI::startTimer()
+{
+ switch (state) {
+ case Thinking: timer.start(thinkTime, true); break;
+ case GivingOrders: timer.start(orderTime, true); break;
+ }
+}
+
+void AI::timeout()
+{
+ switch (state) {
+ case Thinking:
+ if ( think() ) state = GivingOrders;
+ break;
+ case GivingOrders:
+ if ( emitOrder() ) return;
+ break;
+ }
+
+ startTimer();
+}
+
+bool AI::emitOrder()
+{
+ if ( bestRot==3 ) {
+ bestRot = 0;
+ main->pRotateRight();
+ } else if (bestRot) {
+ bestRot--;
+ main->pRotateLeft();
+ } else if ( bestDec>0 ) {
+ bestDec--;
+ main->pMoveRight();
+ } else if ( bestDec<0 ) {
+ bestDec++;
+ main->pMoveLeft();
+ } else {
+ main->pDropDownStart();
+ return true;
+ }
+ return false;
+}
+
+bool AI::think()
+{
+ initThink();
+ bool moveOk = true;
+ for (uint i=0; i<pieces.size(); i++)
+ if ( !pieces[i]->place() ) {
+ moveOk = false;
+ break;
+ }
+ if (moveOk) {
+ double p = points();
+ if ( !hasBestPoints || p>bestPoints
+ || (p==hasBestPoints && random.getBool()) ) {
+ hasBestPoints = true;
+ bestPoints = p;
+ bestDec = pieces[0]->dec();
+ bestRot = pieces[0]->rot();
+ }
+ }
+
+ for (uint i=pieces.size(); i>0; i--)
+ if ( pieces[i-1]->increment() ) return false;
+ return true;
+}
+
+double AI::points() const
+{
+ double pts = 0;
+ for (uint i=0; i<_elements.size(); i++) {
+ if ( _elements[i].coefficient==0.0 ) continue;
+ double v = _elements[i].data->function(*main, *board);
+ if ( _elements[i].data->triggered && qRound(v)<_elements[i].trigger )
+ continue;
+ pts += _elements[i].coefficient * v;
+ }
+ return pts;
+}
+
+void AI::settingsChanged()
+{
+ int d = CommonPrefs::thinkingDepth();
+ resizePieces(d);
+ for (uint i=0; i<_elements.size(); i++) {
+ const Data &data = *_elements[i].data;
+ _elements[i].coefficient = AIConfig::coefficient(data);
+ if (data.triggered) _elements[i].trigger = AIConfig::trigger(data);
+ }
+ if ( timer.isActive() ) launch(main);
+}
+
+double AI::nbOccupiedLines(const Board &, const Board &current)
+{
+ return current.matrix().height() - current.nbClearLines();
+}
+
+double AI::nbHoles(const Board &, const Board &current)
+{
+ uint nb = 0;
+ for (uint i=0; i<current.matrix().width(); i++) {
+ for (int j=current.firstColumnBlock(i)-1; j>=0; j--) {
+ KGrid2D::Coord c(i, j);
+ if ( current.matrix()[c]==0 ) nb++;
+ }
+ }
+ return nb;
+}
+
+double AI::peakToPeak(const Board &, const Board &current)
+{
+ int min = current.matrix().height()-1;
+ for (uint i=0; i<current.matrix().width(); i++)
+ min = kMin(min, current.firstColumnBlock(i));
+ return (int)current.firstClearLine()-1 - min;
+}
+
+double AI::mean(const Board &, const Board &current)
+{
+ double mean = 0;
+ for (uint i=0; i<current.matrix().width(); i++)
+ mean += current.firstColumnBlock(i);
+ return mean / current.matrix().width();
+}
+
+double AI::nbSpaces(const Board &main, const Board &current)
+{
+ double nb = 0;
+ double m = mean(main, current);
+ for (uint i=0; i<current.matrix().width(); i++) {
+ int j = current.firstColumnBlock(i);
+ if ( j<m ) nb += m - j;
+ }
+ return nb;
+}
+
+double AI::nbRemoved(const Board &main, const Board &current)
+{
+ return current.nbRemoved() - main.nbRemoved();
+}
+
+
+//-----------------------------------------------------------------------------
+const uint AIConfig::minThinkingDepth = 1;
+const uint AIConfig::maxThinkingDepth = 2;
+
+AIConfig::AIConfig(const QValueVector<AI::Element> &elements)
+ : QWidget(0, "ai config")
+{
+ QGridLayout *top = new QGridLayout(this, 3, 2, KDialog::marginHint(),
+ KDialog::spacingHint());
+
+ QLabel *label = new QLabel(i18n("Thinking depth:"), this);
+ top->addWidget(label, 0, 0);
+ KIntNumInput *in = new KIntNumInput(this, "kcfg_ThinkingDepth");
+ in->setRange(minThinkingDepth, maxThinkingDepth);
+ top->addWidget(in, 0, 1);
+
+ top->addRowSpacing(1, KDialog::spacingHint());
+
+ QGrid *grid = new QGrid(2, this);
+ top->addMultiCellWidget(grid, 2, 2, 0, 1);
+ for (uint i=0; i<elements.size(); i++) {
+ const AI::Data &data = *elements.at(i).data;
+ QLabel *label = new QLabel(i18n(data.label), grid);
+ if (data.whatsthis) QWhatsThis::add(label, i18n(data.whatsthis));
+ label->setFrameStyle(QFrame::Panel | QFrame::Plain);
+
+ QVBox *vb = new QVBox(grid);
+ if (data.whatsthis) QWhatsThis::add(vb, i18n(data.whatsthis));
+ vb->setMargin(KDialog::spacingHint());
+ vb->setSpacing(KDialog::spacingHint());
+ vb->setFrameStyle(QFrame::Panel | QFrame::Plain);
+ if (data.triggered) {
+ KIntNumInput *trig = new KIntNumInput(vb, triggerKey(data.name));
+ trig->setRange(0, 10, 1, true);
+ }
+ KDoubleNumInput *coeff = new KDoubleNumInput(vb, coefficientKey(data.name));
+ coeff->setRange(0.0, 1.0, 1.0, true);
+ }
+}
+
+QCString AIConfig::triggerKey(const char *name)
+{
+ return "kcfg_Trigger_" + QCString(name);
+}
+
+QCString AIConfig::coefficientKey(const char *name)
+{
+ return "kcfg_Coefficient_" + QCString(name);
+}
+
+double AIConfig::coefficient(const AI::Data &data)
+{
+ KConfigSkeletonItem *item = CommonPrefs::self()->findItem( QString("Coefficient_%1").arg(data.name) );
+ assert(item);
+ return item->property().toDouble();
+}
+
+int AIConfig::trigger(const AI::Data &data)
+{
+ KConfigSkeletonItem *item = CommonPrefs::self()->findItem( QString("Trigger_%1").arg(data.name) );
+ assert(item);
+ return item->property().toInt();
+}
diff --git a/libksirtet/common/ai.h b/libksirtet/common/ai.h
new file mode 100644
index 00000000..da298abc
--- /dev/null
+++ b/libksirtet/common/ai.h
@@ -0,0 +1,122 @@
+#ifndef COMMON_AI_H
+#define COMMON_AI_H
+
+#include <qtimer.h>
+#include <qvaluevector.h>
+
+#include <kdialogbase.h>
+#include <knuminput.h>
+#include <krandomsequence.h>
+#include "lib/libksirtet_export.h"
+
+class Board;
+class Piece;
+
+
+//-----------------------------------------------------------------------------
+class LIBKSIRTET_EXPORT AIPiece
+{
+ public:
+ AIPiece();
+ ~AIPiece();
+
+ void init(const Piece *p, Board *b);
+ bool place();
+ bool increment();
+
+ int dec() const { return curDec; }
+ uint rot() const { return curRot; }
+
+ private:
+ uint nbPos, nbRot, curPos, curRot;
+ int curDec;
+ const Piece *_piece;
+ Piece *_current;
+ Board *_board;
+
+ void reset();
+};
+
+//-----------------------------------------------------------------------------
+class LIBKSIRTET_EXPORT AI : public QObject
+{
+ Q_OBJECT
+ public:
+ struct Data {
+ const char *name, *label, *whatsthis;
+ bool triggered;
+ double (*function)(const Board &, const Board &);
+ };
+ static const Data LastData;
+
+ AI(uint thinkTime, uint orderTime, const Data *DATA);
+ virtual ~AI();
+
+ void launch(Board *main);
+ void stop();
+ void start();
+
+ class Element {
+ public:
+ const Data *data;
+ double coefficient;
+ int trigger;
+ };
+ const QValueVector<Element> &elements() const { return _elements; }
+
+ void settingsChanged();
+
+ private slots:
+ void timeout();
+
+ protected:
+ virtual void initThink();
+
+ static double nbOccupiedLines(const Board &, const Board &);
+ static double nbHoles(const Board &, const Board &);
+ static double nbSpaces(const Board &, const Board &);
+ static double peakToPeak(const Board &, const Board &);
+ static double mean(const Board &, const Board &);
+ static double nbRemoved(const Board &, const Board &);
+
+ private:
+ bool think();
+ void startTimer();
+ bool emitOrder();
+ double points() const;
+ void resizePieces(uint size);
+
+ QTimer timer;
+ enum ThinkState { Thinking, GivingOrders };
+ ThinkState state;
+ uint thinkTime, orderTime;
+ bool stopped;
+ QMemArray<AIPiece *> pieces;
+ QValueVector<Element> _elements;
+ Board *main, *board;
+ KRandomSequence random;
+
+ bool hasBestPoints;
+ double bestPoints;
+ int bestDec;
+ uint bestRot;
+};
+
+//-----------------------------------------------------------------------------
+class LIBKSIRTET_EXPORT AIConfig : public QWidget
+{
+ Q_OBJECT
+ public:
+ AIConfig(const QValueVector<AI::Element> &elements);
+
+ static double coefficient(const AI::Data &data);
+ static int trigger(const AI::Data &data);
+
+ private:
+ static QCString triggerKey(const char *name);
+ static QCString coefficientKey(const char *name);
+
+ static const uint minThinkingDepth, maxThinkingDepth;
+};
+
+#endif
diff --git a/libksirtet/common/board.cpp b/libksirtet/common/board.cpp
new file mode 100644
index 00000000..f5f011a6
--- /dev/null
+++ b/libksirtet/common/board.cpp
@@ -0,0 +1,286 @@
+#include "board.h"
+#include "board.moc"
+
+#include <knotifyclient.h>
+#include <klocale.h>
+
+#include "factory.h"
+#include "base/piece.h"
+#include "misc_ui.h"
+#include "ai.h"
+#include "commonprefs.h"
+
+
+Board::Board(bool graphic, GiftPool *gp, QWidget *parent)
+ : BaseBoard(graphic, parent),
+ _giftPool(gp), aiEngine(0)
+{}
+
+Board::~Board()
+{
+ delete aiEngine;
+}
+
+void Board::setType(bool _ai)
+{
+ Q_ASSERT( graphic() );
+ if (_ai) {
+ if ( aiEngine==0 ) aiEngine = cfactory->createAI();
+ } else {
+ delete aiEngine;
+ aiEngine = 0;
+ }
+}
+
+void Board::start(const GTInitData &data)
+{
+ randomGarbage.setSeed(data.seed);
+ _giftPool->reset();
+ BaseBoard::start(data);
+}
+
+void Board::stop()
+{
+ BaseBoard::stop();
+ if (aiEngine) aiEngine->stop();
+}
+
+void Board::showBoard(bool show)
+{
+ BaseBoard::showBoard(show);
+ showCanvas(_next, show);
+}
+
+void Board::unpause()
+{
+ BaseBoard::unpause();
+ if (aiEngine) aiEngine->start(); // eventually restart thinking
+}
+
+void Board::updateLevel()
+{
+ uint nb = cfactory->cbi.nbRemovedToLevel;
+ if ( nbRemoved()>=level()*nb ) updateLevel(level()+1);
+}
+
+void Board::updateLevel(uint newLevel)
+{
+ BaseBoard::updateLevel(newLevel);
+ emit levelUpdated();
+ if ( graphic() ) startTimer();
+}
+
+void Board::settingsChanged()
+{
+ BaseBoard::settingsChanged();
+ if (aiEngine) aiEngine->settingsChanged();
+}
+
+/*****************************************************************************/
+void Board::pMoveLeft()
+{
+ if ( state!=Normal ) return;
+ moveLeft();
+ main->update();
+}
+
+void Board::pMoveRight()
+{
+ if ( state!=Normal ) return;
+ moveRight();
+ main->update();
+}
+
+void Board::pMoveLeftTotal()
+{
+ if ( state!=Normal ) return;
+ moveLeft(bfactory->bbi.width);
+ main->update();
+}
+
+void Board::pMoveRightTotal()
+{
+ if ( state!=Normal ) return;
+ moveRight(bfactory->bbi.width);
+ main->update();
+}
+
+void Board::pOneLineDown()
+{
+ if ( state!=Normal ) return;
+ oneLineDown();
+ main->update();
+}
+
+void Board::pDropDownStart()
+{
+ if ( state!=Normal ) return;
+ _dropHeight = 0;
+ oneLineDown();
+ if ( state==Normal ) {
+ state = DropDown;
+ startTimer();
+ }
+ main->update();
+}
+
+void Board::pDropDownStop()
+{
+ if ( state!=DropDown || CommonPrefs::directDropDownEnabled() ) return;
+ state = Normal;
+ startTimer();
+ main->update();
+}
+
+void Board::pRotateLeft()
+{
+ if ( state!=Normal ) return;
+ rotateLeft();
+ main->update();
+}
+
+void Board::pRotateRight()
+{
+ if ( state!=Normal ) return;
+ rotateRight();
+ main->update();
+}
+
+void Board::pieceDropped(uint dropHeight)
+{
+ if ( state==DropDown ) state = Normal;
+ else _dropHeight = dropHeight;
+ _beforeGlue(true);
+}
+
+void Board::_beforeGlue(bool first)
+{
+ if ( graphic() ) {
+ state = (beforeGlue(_dropHeight>=1, first) ? BeforeGlue : Normal);
+ if ( state==BeforeGlue ) {
+ startTimer();
+ return;
+ }
+ }
+ gluePiece();
+}
+
+void Board::gluePiece()
+{
+ BaseBoard::gluePiece();
+ _afterGlue(true);
+ if ( graphic() ) KNotifyClient::event(winId(), "glued", i18n("Piece glued"));
+}
+
+void Board::_afterGlue(bool first)
+{
+ bool b = afterGlue(!graphic(), first);
+ if ( graphic() ) {
+ state = (b ? AfterGlue : Normal);
+ if ( state==AfterGlue ) {
+ startTimer();
+ return;
+ }
+ }
+
+ updateScore(score() + _dropHeight);
+ if ( needRemoving() ) _beforeRemove(true);
+ else _afterAfterRemove();
+}
+
+bool Board::afterAfterRemove()
+{
+ // checkGift
+ if ( graphic() && _giftPool->pending() ) {
+ if ( putGift(_giftPool->take()) ) {
+ computeInfos();
+ _afterGift(true);
+ return true;
+ } else return false;
+ }
+ return newPiece();
+}
+
+void Board::_afterGift(bool first)
+{
+ Q_ASSERT( graphic() );
+ state = (afterGift(first) ? AfterGift : Normal);
+ if ( state==AfterGift ) startTimer();
+ else afterAfterRemove();
+}
+
+bool Board::newPiece()
+{
+ Q_ASSERT( !graphic() || state==Normal );
+ if ( !BaseBoard::newPiece() ) return false;
+ if ( graphic() ) {
+ main->update();
+ _next->update();
+ if (aiEngine) aiEngine->launch(this);
+ // else : a human player can think by himself ...
+ }
+ return true;
+}
+
+bool Board::timeout()
+{
+ if ( BaseBoard::timeout() ) return true;
+
+ switch (state) {
+ case DropDown: _dropHeight++;
+ case Normal: oneLineDown(); break;
+ case BeforeGlue: _beforeGlue(false); break;
+ case AfterGlue: _afterGlue(false); break;
+ case AfterGift: _afterGift(false); break;
+ default: return false;
+ }
+ main->update();
+ return true;
+}
+
+uint Board::normalTime() const
+{
+ return cfactory->cbi.baseTime / (1 + level());
+}
+
+bool Board::startTimer()
+{
+ if ( BaseBoard::startTimer() ) return true;
+
+ switch (state) {
+ case Normal:
+ timer.start(normalTime());
+ break;
+ case DropDown:
+ timer.start(cfactory->cbi.dropDownTime);
+ break;
+ case BeforeGlue:
+ timer.start(cfactory->cbi.beforeGlueTime, true);
+ break;
+ case AfterGlue:
+ timer.start(cfactory->cbi.afterGlueTime, true);
+ break;
+ case AfterGift:
+ timer.start(cfactory->cbi.afterGiftTime, true);
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+bool Board::beforeGlue(bool bump, bool first)
+{
+ if ( !bump ) return false;
+ if (first) {
+ loop = 0;
+ return true;
+ } else loop++;
+
+ float dec = BasePrefs::blockSize() * (loop+1) * -0.1;
+ if ( BasePrefs::animationsEnabled() ) bumpCurrentPiece((int)dec);
+
+ return ( loop!=cfactory->cbi.nbBumpStages );
+}
diff --git a/libksirtet/common/board.h b/libksirtet/common/board.h
new file mode 100644
index 00000000..97c37c17
--- /dev/null
+++ b/libksirtet/common/board.h
@@ -0,0 +1,75 @@
+#ifndef COMMON_BOARD_H
+#define COMMON_BOARD_H
+
+#include "base/board.h"
+
+#include "lib/libksirtet_export.h"
+
+class GiftPool;
+class AI;
+
+class LIBKSIRTET_EXPORT Board : public BaseBoard
+{
+ Q_OBJECT
+ public:
+ Board(bool graphic, GiftPool *, QWidget *parent);
+ virtual ~Board();
+
+ void setType(bool computer);
+ virtual void start(const GTInitData &);
+ void unpause();
+ void stop();
+
+ virtual uint gift() = 0;
+ virtual bool needRemoving() = 0;
+
+ GiftPool *giftPool() const { return _giftPool; }
+
+ public slots:
+ void pMoveLeft();
+ void pMoveRight();
+ void pDropDownStart();
+ void pDropDownStop();
+ void pOneLineDown();
+ void pRotateLeft();
+ void pRotateRight();
+ void pMoveLeftTotal();
+ void pMoveRightTotal();
+
+ private slots:
+ bool timeout();
+
+ signals:
+ void levelUpdated();
+
+ protected:
+ KRandomSequence randomGarbage;
+
+ virtual bool beforeGlue(bool bump, bool first);
+ virtual void gluePiece();
+ virtual bool afterGlue(bool /*doAll*/, bool /*first*/) { return false; }
+ virtual bool afterGift(bool /*first*/) { return false; }
+ virtual bool putGift(uint) = 0;
+
+ virtual uint normalTime() const;
+ void updateLevel();
+
+ void settingsChanged();
+
+ private:
+ uint _dropHeight;
+ GiftPool *_giftPool;
+ AI *aiEngine;
+
+ bool newPiece();
+ void pieceDropped(uint dropHeight);
+ void _afterGift(bool first);
+ void _beforeGlue(bool first);
+ void _afterGlue(bool first);
+ bool afterAfterRemove();
+ bool startTimer();
+ void showBoard(bool show);
+ void updateLevel(uint newLevel);
+};
+
+#endif
diff --git a/libksirtet/common/commonprefs.kcfgc b/libksirtet/common/commonprefs.kcfgc
new file mode 100644
index 00000000..8e41b16f
--- /dev/null
+++ b/libksirtet/common/commonprefs.kcfgc
@@ -0,0 +1,9 @@
+# Code generation options for kconfig_compiler
+File=libksirtet2.kcfg
+IncludeFiles=base/baseprefs.h
+ClassName=CommonPrefs
+Inherits=BasePrefs
+Singleton=true
+#Mutators=true
+#CustomAdditions=true
+Mutators=false
diff --git a/libksirtet/common/factory.cpp b/libksirtet/common/factory.cpp
new file mode 100644
index 00000000..1b239a82
--- /dev/null
+++ b/libksirtet/common/factory.cpp
@@ -0,0 +1,28 @@
+#include "factory.h"
+
+#include "ai.h"
+#include "settings.h"
+
+
+CommonFactory::CommonFactory(const MainData &md, const BaseBoardInfo &bbi,
+ const CommonBoardInfo &ci)
+ : BaseFactory(md, bbi), cbi(ci)
+{}
+
+QWidget *CommonFactory::createAppearanceConfig()
+{
+ return new AppearanceConfig;
+}
+
+QWidget *CommonFactory::createGameConfig()
+{
+ return new GameConfig;
+}
+
+QWidget *CommonFactory::createAIConfig()
+{
+ AI *ai = createAI();
+ QWidget *cw = new AIConfig(ai->elements());
+ delete ai;
+ return cw;
+}
diff --git a/libksirtet/common/factory.h b/libksirtet/common/factory.h
new file mode 100644
index 00000000..c0b40d66
--- /dev/null
+++ b/libksirtet/common/factory.h
@@ -0,0 +1,36 @@
+#ifndef COMMON_FACTORY_H
+#define COMMON_FACTORY_H
+
+#include "base/factory.h"
+
+#include "lib/libksirtet_export.h"
+
+struct CommonBoardInfo {
+ uint baseTime, dropDownTime, beforeGlueTime, afterGlueTime;
+ uint afterGiftTime, nbBumpStages;
+ uint nbRemovedToLevel;
+ uint nbGiftLeds, maxGiftsToSend, giftShowerTimeout, giftPoolTimeout;
+};
+
+class BaseField;
+class AI;
+
+#define cfactory static_cast<CommonFactory *>(BaseFactory::self())
+
+class LIBKSIRTET_EXPORT CommonFactory : public BaseFactory
+{
+ public:
+ CommonFactory(const MainData &, const BaseBoardInfo &,
+ const CommonBoardInfo &);
+
+ const CommonBoardInfo &cbi;
+
+ virtual BaseField *createField(QWidget *parent) = 0;
+ virtual AI *createAI() = 0;
+
+ QWidget *createAIConfig();
+ virtual QWidget *createAppearanceConfig();
+ virtual QWidget *createGameConfig();
+};
+
+#endif
diff --git a/libksirtet/common/field.cpp b/libksirtet/common/field.cpp
new file mode 100644
index 00000000..2d67062e
--- /dev/null
+++ b/libksirtet/common/field.cpp
@@ -0,0 +1,243 @@
+#include "field.h"
+#include "field.moc"
+
+#include <qwhatsthis.h>
+#include <qlabel.h>
+#include <qlayout.h>
+
+#include <klocale.h>
+#include <kprogress.h>
+#include <kgameprogress.h>
+#include <kcanvasrootpixmap.h>
+#include <kgamelcd.h>
+
+#include "base/baseprefs.h"
+#include "factory.h"
+#include "highscores.h"
+#include "misc_ui.h"
+#include "board.h"
+#include "commonprefs.h"
+
+
+Field::Field(QWidget *parent)
+: MPSimpleBoard(parent), BaseField(this)
+{
+// column 1
+ // score LCD
+ scoreList = new KGameLCDList(this);
+ showScore = new KGameLCD(6, scoreList);
+ scoreList->append(showScore);
+ showTime = new KGameLCDClock(scoreList);
+ scoreList->append(showTime);
+ lcds->addWidget(scoreList, 1, 0);
+ lcds->setRowStretch(2, 1);
+
+ // removed LCD
+ removedList = new KGameLCDList(i18n(bfactory->mainData.removedLabel), this);
+ lcds->addWidget(removedList, 3, 0);
+ lcds->setRowStretch(4, 1);
+
+ // level progress
+ levelLabel = new QLabel(this);
+ levelLabel->setAlignment(AlignCenter);
+ lcds->addWidget(levelLabel, 5, 0);
+ toLevel = new KProgress(this);
+ toLevel->setTextEnabled(true);
+ toLevel->setFormat("1");
+ QWhatsThis::add(toLevel, i18n("Display the progress to complete the current level or stage."));
+ lcds->addWidget(toLevel, 6, 0);
+ lcds->setRowStretch(7, 1);
+
+// column 2
+ // previous player height
+ prevHeight = new PlayerProgress(board, this, "prev_progress");
+ QWhatsThis::add(prevHeight, i18n("Previous player's height"));
+ top->addWidget(prevHeight, 1, 1, AlignHCenter);
+
+// column 3
+ // pending gift shower
+ Board *b = static_cast<Board *>(board);
+ top->addWidget(b->giftPool(), 0, 2, AlignCenter);
+
+ // shadow piece
+ shadow = new Shadow(board, this);
+ QWhatsThis::add(shadow, i18n("Shadow of the current piece"));
+ top->addWidget(shadow, 2, 2);
+
+// column 4
+ // next player height
+ nextHeight = new PlayerProgress(board, this, "next_progress");
+ QWhatsThis::add(nextHeight, i18n("Next player's height"));
+ top->addWidget(nextHeight, 1, 3, AlignHCenter);
+
+// column 5
+ // next piece shower
+ QVBoxLayout *vbl = new QVBoxLayout(10);
+ top->addLayout(vbl, 1, 4);
+ vbl->addStretch(1);
+
+ labShowNext = new QLabel(i18n("Next Tile"), this);
+ labShowNext->setAlignment(AlignCenter);
+ vbl->addWidget(labShowNext, 0);
+ showNext = new ShowNextPiece(board, this);
+ _snRootPixmap = new KCanvasRootPixmap(showNext);
+ _snRootPixmap->start();
+ vbl->addWidget(showNext, 0);
+ vbl->addStretch(4);
+
+ connect(board, SIGNAL(scoreUpdated()), SLOT(scoreUpdatedSlot()));
+ connect(board, SIGNAL(levelUpdated()), SLOT(levelUpdated()));
+ connect(board, SIGNAL(removedUpdated()), SLOT(removedUpdated()));
+
+ initVariableGUI();
+}
+
+void Field::levelUpdated()
+{
+ toLevel->setFormat(QString::number(board->level()));
+ // necessary to update string ...
+ int p = toLevel->progress();
+ toLevel->setProgress(p+1);
+ toLevel->setProgress(p);
+}
+
+void Field::removedUpdated()
+{
+ uint nb = cfactory->cbi.nbRemovedToLevel;
+ toLevel->setProgress(isArcade() ? board->arcadeDone()
+ : board->nbRemoved() % nb);
+}
+
+void Field::showOpponents(bool show)
+{
+ Board *b = static_cast<Board *>(board);
+ if (show) {
+ prevHeight->show();
+ nextHeight->show();
+ b->giftPool()->show();
+ } else {
+ prevHeight->hide();
+ nextHeight->hide();
+ b->giftPool()->hide();
+ }
+}
+
+void Field::settingsChanged()
+{
+ BaseField::settingsChanged();
+ QColor color = BasePrefs::fadeColor();
+ double s = BasePrefs::fadeIntensity();
+ _snRootPixmap->setFadeEffect(s, color);
+ showNext->canvas()->setBackgroundColor(color);
+ bool b = CommonPrefs::showNextPiece();
+ if (b) {
+ showNext->show();
+ labShowNext->show();
+ } else {
+ showNext->hide();
+ labShowNext->hide();
+ }
+ b = CommonPrefs::showPieceShadow();
+ if (b) shadow->show();
+ else shadow->hide();
+}
+
+void Field::_init(bool AI, bool multiplayer, bool server, bool first,
+ const QString &name)
+{
+ BaseField::init(AI, multiplayer, server, first, name);
+ showOpponents(multiplayer);
+ static_cast<Board *>(board)->setType(AI);
+}
+
+void Field::_initFlag(QDataStream &s)
+{
+ ServerInitData sid;
+ s >> sid;
+ GTInitData data;
+ data.seed = sid.seed;
+ data.initLevel = sid.initLevel;
+
+ shadow->setDisplay(true);
+ toLevel->setValue(0);
+ showTime->reset();
+ showTime->start();
+
+ BaseField::start(data);
+ initVariableGUI();
+}
+
+void Field::initVariableGUI()
+{
+ if ( board->isArcade() ) {
+ scoreList->title()->setText(i18n("Elapsed time"));
+ showScore->hide();
+ showTime->show();
+ QWhatsThis::add(scoreList, i18n("Display the elapsed time."));
+ levelLabel->setText(i18n("Stage"));
+ toLevel->setTotalSteps( board->arcadeTodo() );
+ } else {
+ scoreList->title()->setText(i18n("Score"));
+ showScore->show();
+ showTime->hide();
+ QWhatsThis::add(scoreList, i18n("<qt>Display the current score.<br/>It turns <font color=\"blue\">blue</font> if it is a highscore and <font color=\"red\">red</font> if it is the best local score.</qt>"));
+ levelLabel->setText(i18n("Level"));
+ toLevel->setTotalSteps(cfactory->cbi.nbRemovedToLevel);
+ }
+}
+
+void Field::_playFlag(QDataStream &s)
+{
+ ServerPlayData spd;
+ s >> spd;
+ prevHeight->setValue(spd.prevHeight);
+ nextHeight->setValue(spd.nextHeight);
+ if (spd.gift)
+ static_cast<Board *>(board)->giftPool()->put(spd.gift);
+}
+
+void Field::_pauseFlag(bool p)
+{
+ pause(p);
+ shadow->setDisplay(!p);
+ if (p) showTime->stop();
+ else showTime->start();
+}
+
+void Field::_stopFlag(bool gameover)
+{
+ BaseField::stop(gameover);
+ showTime->stop();
+}
+
+void Field::_dataOut(QDataStream &s)
+{
+ _cpd.height = board->firstClearLine();
+ _cpd.end = static_cast<Board *>(board)->isGameOver();
+ _cpd.gift = static_cast<Board *>(board)->gift();
+ s << _cpd;
+}
+
+KExtHighscore::Score Field::currentScore() const
+{
+ KExtHighscore::Score score(_cpd.end ? KExtHighscore::Lost : KExtHighscore::Won);
+ score.setScore(board->score());
+ score.setData("level", board->level());
+ score.setData("removed", board->nbRemoved());
+ return score;
+}
+
+void Field::_gameOverDataOut(QDataStream &s)
+{
+ s << currentScore();
+}
+
+void Field::moveLeft() { static_cast<Board *>(board)->pMoveLeft(); }
+void Field::moveRight() { static_cast<Board *>(board)->pMoveRight(); }
+void Field::dropDownStart() { static_cast<Board *>(board)->pDropDownStart(); }
+void Field::dropDownStop() { static_cast<Board *>(board)->pDropDownStop(); }
+void Field::oneLineDown() { static_cast<Board *>(board)->pOneLineDown(); }
+void Field::rotateLeft() { static_cast<Board *>(board)->pRotateLeft(); }
+void Field::rotateRight() { static_cast<Board *>(board)->pRotateRight(); }
+void Field::moveLeftTotal() { static_cast<Board *>(board)->pMoveLeftTotal(); }
+void Field::moveRightTotal() { static_cast<Board *>(board)->pMoveRightTotal();}
diff --git a/libksirtet/common/field.h b/libksirtet/common/field.h
new file mode 100644
index 00000000..8179539b
--- /dev/null
+++ b/libksirtet/common/field.h
@@ -0,0 +1,68 @@
+#ifndef COMMON_FIELD_H
+#define COMMON_FIELD_H
+
+#include "lib/mp_simple_board.h"
+#include "base/field.h"
+#include "types.h"
+
+#include "lib/libksirtet_export.h"
+
+
+class ShowNextPiece;
+class GiftShower;
+class Shadow;
+class KProgress;
+class KGameProgress;
+class KGameLCDClock;
+
+class LIBKSIRTET_EXPORT Field : public MPSimpleBoard, public BaseField
+{
+ Q_OBJECT
+ public:
+ Field(QWidget *parent);
+
+ public slots:
+ void moveLeft();
+ void moveRight();
+ void dropDownStart();
+ void dropDownStop();
+ void oneLineDown();
+ void rotateLeft();
+ void rotateRight();
+ void moveLeftTotal();
+ void moveRightTotal();
+
+ virtual void settingsChanged();
+
+ protected slots:
+ void scoreUpdatedSlot() { scoreUpdated(); }
+ virtual void levelUpdated();
+ virtual void removedUpdated();
+
+ private:
+ KGameLCDClock *showTime;
+ ShowNextPiece *showNext;
+ KProgress *toLevel;
+ QLabel *labShowNext, *levelLabel;
+ KGameProgress *prevHeight, *nextHeight;
+ Shadow *shadow;
+ KCanvasRootPixmap *_snRootPixmap;
+ ClientPlayData _cpd;
+
+ void _init(bool AI, bool multiplayer, bool server, bool first,
+ const QString &name);
+ void showOpponents(bool show);
+ void initVariableGUI();
+
+ void _initFlag(QDataStream &);
+ void _playFlag(QDataStream &);
+ void _pauseFlag(bool pause);
+ void _stopFlag(bool gameover);
+ void _dataOut(QDataStream &);
+ void _gameOverDataOut(QDataStream &);
+ void _initDataOut(QDataStream &) {}
+
+ KExtHighscore::Score currentScore() const;
+};
+
+#endif
diff --git a/libksirtet/common/highscores.cpp b/libksirtet/common/highscores.cpp
new file mode 100644
index 00000000..799a31b9
--- /dev/null
+++ b/libksirtet/common/highscores.cpp
@@ -0,0 +1,60 @@
+#include "highscores.h"
+
+#include <klocale.h>
+#include <kapplication.h>
+#include <kconfig.h>
+
+#include "base/factory.h"
+
+
+using namespace KExtHighscore;
+
+CommonHighscores::CommonHighscores()
+{
+ Item *item = new Item((uint)1, i18n("Level"), Qt::AlignRight);
+ addScoreItem("level", item);
+ item = new Item((uint)0, i18n(bfactory->mainData.removedLabel),
+ Qt::AlignRight);
+ addScoreItem("removed", item);
+}
+
+void CommonHighscores::convertLegacy(uint)
+{
+ KConfigGroupSaver cg(kapp->config(), "High Scores");
+ for (uint i=0; i<10; i++) {
+ QString name
+ = cg.config()->readEntry(QString("name%1").arg(i), QString::null);
+ if ( name.isNull() ) break;
+ if ( name.isEmpty() ) name = i18n("anonymous");
+ uint score
+ = cg.config()->readUnsignedNumEntry(QString("score%1").arg(i), 0);
+ uint level
+ = cg.config()->readUnsignedNumEntry(QString("level%1").arg(i), 1);
+ Score s(Won);
+ s.setScore(score);
+ s.setData("name", name);
+ s.setData("level", level);
+ submitLegacyScore(s);
+ }
+}
+
+bool CommonHighscores::isStrictlyLess(const Score &s1, const Score &s2) const
+{
+ uint l1 = s1.data("level").toUInt();
+ uint r1 = s1.data("removed").toUInt();
+ uint l2 = s2.data("level").toUInt();
+ uint r2 = s2.data("removed").toUInt();
+
+ if ( s1.score()==s2.score() ) {
+ if ( l1==l2 ) return r1<r2;
+ else return l1<l2;
+ } else return BaseHighscores::isStrictlyLess(s1, s2);
+}
+
+void CommonHighscores::additionalQueryItems(KURL &url, const Score &s) const
+{
+ uint l = s.data("level").toUInt();
+ addToQueryURL(url, "scoreLevel", QString::number(l));
+ uint r = s.data("removed").toUInt();
+ addToQueryURL(url, "scoreRemoved", QString::number(r));
+}
diff --git a/libksirtet/common/highscores.h b/libksirtet/common/highscores.h
new file mode 100644
index 00000000..f04c0d2e
--- /dev/null
+++ b/libksirtet/common/highscores.h
@@ -0,0 +1,20 @@
+#ifndef COMMON_HIGHSCORES_H
+#define COMMON_HIGHSCORES_H
+
+#include "base/highscores.h"
+
+#include "lib/libksirtet_export.h"
+
+class LIBKSIRTET_EXPORT CommonHighscores : public BaseHighscores
+{
+ public:
+ CommonHighscores();
+
+ private:
+ void convertLegacy(uint level);
+ bool isStrictlyLess(const KExtHighscore::Score &,
+ const KExtHighscore::Score &) const;
+ void additionalQueryItems(KURL &, const KExtHighscore::Score &) const;
+};
+
+#endif
diff --git a/libksirtet/common/inter.cpp b/libksirtet/common/inter.cpp
new file mode 100644
index 00000000..e9f1688b
--- /dev/null
+++ b/libksirtet/common/inter.cpp
@@ -0,0 +1,150 @@
+#include "inter.h"
+#include "inter.moc"
+
+#include <klocale.h>
+#include <kaction.h>
+#include <kapplication.h>
+
+#include "factory.h"
+#include "field.h"
+#include "commonprefs.h"
+#include "main.h"
+
+
+const ActionData Interface::ACTION_DATA[Nb_Actions] = {
+ { I18N_NOOP("Move Left"), "move left", SLOT(moveLeft()), 0 },
+ { I18N_NOOP("Move Right"), "move right", SLOT(moveRight()), 0 },
+ { I18N_NOOP("Drop Down"), "drop down", SLOT(dropDownStart()),
+ SLOT(dropDownStop()) },
+ { I18N_NOOP("One Line Down"), "one line down", SLOT(oneLineDown()), 0 },
+ { I18N_NOOP("Rotate Left"), "rotate left", SLOT(rotateLeft()), 0 },
+ { I18N_NOOP("Rotate Right"), "rotate right", SLOT(rotateRight()), 0 },
+ { I18N_NOOP("Move to Left Column"), "move left total",
+ SLOT(moveLeftTotal()), 0 },
+ { I18N_NOOP("Move to Right Column"), "move right total",
+ SLOT(moveRightTotal()), 0 }
+};
+
+const int Interface::KEYCODE_ONE[Nb_Actions] = {
+ Key_Left, Key_Right, Key_Down, Key_Shift, Key_Up, Key_Return,
+ CTRL+Key_Left, CTRL+Key_Right
+};
+const int Interface::KEYCODE_TWO[Nb_Actions] = {
+ Key_F, Key_G, Key_D, Key_Space, Key_E, Key_C, SHIFT+Key_F, SHIFT+Key_G
+};
+
+Interface::Interface(const MPGameInfo &gi, QWidget *parent)
+ : MPSimpleInterface(gi, Nb_Actions, ACTION_DATA, parent)
+{
+ setDefaultKeycodes(1, 0, KEYCODE_ONE);
+ setDefaultKeycodes(2, 0, KEYCODE_TWO);
+ setDefaultKeycodes(2, 1, KEYCODE_ONE);
+}
+
+MPBoard *Interface::newBoard(uint i)
+{
+ Field *f = static_cast<Field *>(cfactory->createField(this));
+ f->settingsChanged();
+ connect(this, SIGNAL(settingsChanged()), f, SLOT(settingsChanged()));
+ if ( i==0 ) _firstField = f;
+ return f;
+}
+
+void Interface::normalGame()
+{
+ singleHuman();
+}
+
+void Interface::arcadeGame()
+{
+ singleHuman();
+ _firstField->setArcade();
+}
+
+void Interface::_init()
+{
+ if ( !server() ) return;
+ _data.resize(nbPlayers());
+ _scores.setPlayerCount( nbPlayers() );
+ for (uint i=0; i<nbPlayers(); i++)
+ _scores.setName(i, playerName(i));
+}
+
+bool Interface::_readPlayData()
+{
+ bool end = false;
+ for (uint i=0; i<nbPlayers(); i++) {
+ readingStream(i) >> _data[i];
+ if (_data[i].end) end = true;
+ }
+ return end;
+}
+
+void Interface::_sendPlayData()
+{
+ ServerPlayData sd;
+ for(uint i=0; i<nbPlayers(); i++) {
+ sd.prevHeight = _data[prev(i)].height;
+ sd.nextHeight = _data[next(i)].height;
+ sd.gift = _data[prev(i)].gift;
+ writingStream(i) << sd;
+ }
+}
+
+void Interface::_showHighscores(QWidget *parent)
+{
+ if ( !server() || nbPlayers()!=1 ) _scores.show(parent);
+ else BaseInterface::_showHighscores(parent);
+}
+
+void Interface::_showGameOverData()
+{
+ if ( !server() || nbPlayers()!=1 ) _scores.show(this);
+ else if ( !_firstField->isArcade() ) {
+ _score.setType(KExtHighscore::Won);
+ BaseField::gameOver(_score, this);
+ }
+}
+
+uint Interface::prev(uint i) const
+{
+ if ( i==0 ) return nbPlayers()-1;
+ else return i-1;
+}
+
+uint Interface::next(uint i) const
+{
+ if ( i==(nbPlayers()-1) ) return 0;
+ else return i+1;
+}
+
+// server only
+void Interface::_treatInit()
+{
+ ServerInitData sid;
+ sid.seed = kapp->random();
+ sid.initLevel = CommonPrefs::initialGameLevel();
+ for (uint i=0; i<nbPlayers(); i++) {
+ sid.prevName = playerName(prev(i));
+ sid.nextName = playerName(next(i));
+ sid.name = playerName(i);
+ writingStream(i) << sid;
+ }
+}
+
+void Interface::_sendGameOverData(QDataStream &s)
+{
+ bool multiplayers = ( nbPlayers()>1 );
+ for (uint i=0; i<nbPlayers(); i++) {
+ readingStream(i) >> _score;
+ if (multiplayers) _scores.addScore(i, _score);
+ }
+ if (multiplayers) s << _scores;
+ // else no need to send anything
+}
+
+// client only
+void Interface::_readGameOverData(QDataStream &s)
+{
+ s >> _scores;
+}
diff --git a/libksirtet/common/inter.h b/libksirtet/common/inter.h
new file mode 100644
index 00000000..e38c2ed5
--- /dev/null
+++ b/libksirtet/common/inter.h
@@ -0,0 +1,62 @@
+#ifndef COMMON_INTER_H
+#define COMMON_INTER_H
+
+#include <kexthighscore.h>
+#include "lib/libksirtet_export.h"
+
+#include "lib/mp_simple_interface.h"
+#include "base/inter.h"
+#include "types.h"
+#include "board.h"
+
+
+class Field;
+
+class LIBKSIRTET_EXPORT Interface : public MPSimpleInterface, public BaseInterface
+{
+ Q_OBJECT
+public:
+ Interface(const MPGameInfo &, QWidget *parent);
+
+signals:
+ void settingsChanged();
+
+public slots:
+ void normalGame();
+ void arcadeGame();
+ void settingsChangedSlot() { emit settingsChanged(); }
+
+protected:
+ void _showHighscores(QWidget *parent);
+
+private:
+ QMemArray<ClientPlayData> _data;
+ KExtHighscore::Score _score;
+ KExtHighscore::MultiplayerScores _scores;
+ Field *_firstField;
+
+ enum Action { Nb_Actions = 8 };
+ static const ActionData ACTION_DATA[Nb_Actions];
+ static const int KEYCODE_ONE[Nb_Actions];
+ static const int KEYCODE_TWO[Nb_Actions];
+
+ MPBoard *newBoard(uint);
+ void setInitData(uint player, ServerInitData &);
+ uint prev(uint i) const;
+ uint next(uint i) const;
+
+ void _readGameOverData(QDataStream &s);
+ void _sendGameOverData(QDataStream &s);
+ void _firstInit() {}
+ void _treatInit();
+ void _init();
+ void _showGameOverData();
+ bool _readPlayData();
+ void _sendPlayData();
+
+ void _start() { MPSimpleInterface::start(); }
+ void _pause() { MPSimpleInterface::pause(); }
+ bool _isPaused() const { return MPSimpleInterface::isPaused(); }
+};
+
+#endif
diff --git a/libksirtet/common/libksirtet2.kcfg b/libksirtet/common/libksirtet2.kcfg
new file mode 100644
index 00000000..27ee72e3
--- /dev/null
+++ b/libksirtet/common/libksirtet2.kcfg
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <group name="Options">
+ <entry name="ShowNextPiece" type="Bool" key="show next piece">
+ <label>Show next piece.</label>
+ <default>true</default>
+ </entry>
+ <entry name="ShowPieceShadow" type="Bool" key="show piece shadow">
+ <label>Show the shadow of a piece.</label>
+ <default>true</default>
+ </entry>
+ <entry name="ShowDetailedRemoved" type="Bool" key="show detailed removed">
+ <label>Show detailed 'removed lines'.</label>
+ <default>false</default>
+ </entry>
+ <entry name="InitialGameLevel" type="Int" key="init level" min="1" max="20">
+ <label>The inital level of new games.</label>
+ <default>1</default>
+ <min>1</min>
+ <max>20</max>
+ </entry>
+ <entry name="DirectDropDownEnabled" type="Bool" key="direct drop down">
+ <label>Enable direct dropping of pieces.</label>
+ <default>false</default>
+ </entry>
+ </group>
+ <group name="AI">
+ <entry name="ThinkingDepth" type="Int" key="thinking depth" min="1" max="2">
+ <label>The thinking depth</label>
+ <default>2</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/libksirtet/common/main.cpp b/libksirtet/common/main.cpp
new file mode 100644
index 00000000..004b86ca
--- /dev/null
+++ b/libksirtet/common/main.cpp
@@ -0,0 +1,60 @@
+#include "main.h"
+#include "main.moc"
+
+#include <klocale.h>
+#include <kaction.h>
+#include <kstdaction.h>
+#include <kkeydialog.h>
+#include <kconfigdialog.h>
+
+#include "inter.h"
+#include "factory.h"
+
+void MainWindow::addConfig(KConfigDialog *dialog)
+{
+ QWidget *w = cfactory->createAIConfig();
+ if (w) dialog->addPage(w, i18n("A.I."), "personal");
+}
+
+void MainWindow::init()
+{
+ Interface *inter = static_cast<Interface *>(_inter);
+ inter->normalGame();
+ setFocusPolicy(StrongFocus);
+
+ // Modes
+ bool ama = ( bfactory->bbi.nbArcadeStages!=0 );
+ QString s = (ama ? i18n("&Single Human (Normal)") : i18n("&Single Human"));
+ (void)new KAction(s, 0, inter, SLOT(normalGame()),
+ actionCollection(), "mp_single_human");
+ if (ama) (void)new KAction(i18n("&Single Human (Arcade)"), 0,
+ inter, SLOT(arcadeGame()),
+ actionCollection(), "mp_arcade");
+ (void)new KAction(i18n("Human vs &Human"), 0, inter, SLOT(humanVsHuman()),
+ actionCollection(), "mp_human_vs_human");
+ (void)new KAction(i18n("Human vs &Computer"), 0,
+ inter, SLOT(humanVsComputer()),
+ actionCollection(), "mp_human_vs_computer");
+ (void)new KAction(i18n("&More..."), 0, inter, SLOT(dialog()),
+ actionCollection(), "mp_more");
+
+ buildGUI(inter);
+ connect(this, SIGNAL(settingsChanged()),
+ inter, SLOT(settingsChangedSlot()));
+}
+
+void MainWindow::addKeys(KKeyDialog &d)
+{
+ static_cast<Interface *>(_inter)->addKeys(d);
+}
+
+void MainWindow::saveKeys()
+{
+ static_cast<Interface *>(_inter)->saveKeys();
+}
+
+void MainWindow::focusInEvent(QFocusEvent *e)
+{
+ static_cast<Interface *>(_inter)->setFocus();
+ BaseMainWindow::focusInEvent(e);
+}
diff --git a/libksirtet/common/main.h b/libksirtet/common/main.h
new file mode 100644
index 00000000..d22b7273
--- /dev/null
+++ b/libksirtet/common/main.h
@@ -0,0 +1,22 @@
+#ifndef COMMON_MAIN_H
+#define COMMON_MAIN_H
+
+#include "base/main.h"
+
+#include "lib/libksirtet_export.h"
+
+class LIBKSIRTET_EXPORT MainWindow : public BaseMainWindow
+{
+ Q_OBJECT
+public:
+ MainWindow() {}
+
+protected:
+ void init();
+ void addConfig(KConfigDialog *);
+ void addKeys(KKeyDialog &);
+ void saveKeys();
+ virtual void focusInEvent(QFocusEvent *e);
+};
+
+#endif
diff --git a/libksirtet/common/misc_ui.cpp b/libksirtet/common/misc_ui.cpp
new file mode 100644
index 00000000..45cfb440
--- /dev/null
+++ b/libksirtet/common/misc_ui.cpp
@@ -0,0 +1,194 @@
+#include "misc_ui.h"
+#include "misc_ui.moc"
+
+#include <qpainter.h>
+
+#include "base/piece.h"
+#include "base/board.h"
+#include "base/baseprefs.h"
+#include "base/kzoommainwindow.h"
+#include "factory.h"
+
+const uint GIFT_SHOWER_TIMEOUT = 800;
+const uint GIFT_POOL_TIMEOUT = 2000;
+
+const uint SHADOW_HEIGHT = 10;
+
+const uint GI_WIDTH = 15;
+const uint GI_HEIGHT = 11;
+const uint ARROW_HEIGHT = 3;
+const uint ARROW_WIDTH = 7;
+
+const uint LED_WIDTH = 15;
+const uint LED_HEIGHT = 15;
+const uint LED_SPACING = 5;
+
+/*****************************************************************************/
+ShowNextPiece::ShowNextPiece(BaseBoard *board, QWidget *parent)
+ : FixedCanvasView(parent, "show_next_piece")
+{
+ setCanvas(board->next());
+ setFrameStyle(QFrame::Panel | QFrame::Sunken);
+ KZoomMainWindow::addWidget(this);
+}
+
+/*****************************************************************************/
+Shadow::Shadow(BaseBoard *board, QWidget *parent)
+ : QWidget(parent, "shadow"), _xOffset(board->frameWidth()),
+ _board(board), _show(false)
+{
+ KZoomMainWindow::addWidget(this);
+ connect(board, SIGNAL(updatePieceConfigSignal()), SLOT(update()));
+}
+
+QSize Shadow::sizeHint() const
+{
+ return QSize(_xOffset + _board->matrix().width() * BasePrefs::blockSize(),
+ SHADOW_HEIGHT);
+}
+
+QSizePolicy Shadow::sizePolicy() const
+{
+ return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+}
+
+void Shadow::setDisplay(bool show)
+{
+ _show = show;
+ update();
+}
+
+void Shadow::paintEvent(QPaintEvent *)
+{
+ if ( !_show ) return;
+
+ const Piece *piece = _board->currentPiece();
+ uint pf = piece->min().first + _board->currentPos().first;
+ uint pl = pf + piece->size().first - 1;
+
+ QPainter p(this);
+ p.setBrush(black);
+ p.setPen(black);
+ for (uint i=pf; i<=pl; i++) {
+ QRect r(_xOffset + i * BasePrefs::blockSize() + 1 , 0,
+ BasePrefs::blockSize() - 2, SHADOW_HEIGHT);
+ p.drawRect(r);
+ }
+}
+
+
+/*****************************************************************************/
+class Led : public QWidget
+{
+ public:
+ Led(const QColor &c, QWidget *parent)
+ : QWidget(parent), col(c), _on(FALSE) {}
+
+ QSizePolicy sizePolicy() const
+ { return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); }
+ QSize sizeHint() const { return QSize(LED_WIDTH, LED_HEIGHT); }
+
+ void on() { if (!_on) { _on = TRUE; repaint(); } }
+ void off() { if (_on) {_on = FALSE; repaint(); } }
+ void setColor(const QColor &c) { if (c!=col) { col = c; repaint(); } }
+
+ protected:
+ void paintEvent(QPaintEvent *) {
+ QPainter p(this);
+ p.setBrush((_on ? col.light() : col.dark()));
+ p.setPen(black);
+ p.drawEllipse(0, 0, width(), height());
+ }
+
+ private:
+ QColor col;
+ bool _on;
+};
+
+GiftPool::GiftPool(QWidget *parent)
+ : QHBox(parent, "gift_pool"), nb(0), ready(false)
+{
+ setSpacing(LED_SPACING);
+ leds.resize(cfactory->cbi.nbGiftLeds);
+ for (uint i=0; i<leds.size(); i++)
+ leds.insert(i, new Led(yellow, this));
+}
+
+QSize GiftPool::sizeHint() const
+{
+ QSize s = (leds.size() ? leds[0]->sizeHint() : QSize());
+ return QSize((s.width()+LED_SPACING)*leds.size()-LED_SPACING, s.height());
+}
+
+QSizePolicy GiftPool::sizePolicy() const
+{
+ return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+}
+
+void GiftPool::put(uint n)
+{
+ if ( n==0 ) return;
+ if ( nb==0 && !ready )
+ QTimer::singleShot(cfactory->cbi.giftPoolTimeout,
+ this, SLOT(timeout()));
+ uint e = QMIN(nb+n, leds.size());
+ for (uint i=nb; i<e; i++) leds[i]->on();
+ uint f = QMIN(nb+n-e, leds.size());
+ for (uint i=0; i<f; i++) leds[i]->setColor(red);
+ nb += n;
+}
+
+uint GiftPool::take()
+{
+ Q_ASSERT(ready);
+ for (uint i=0; i<leds.size(); i++) {
+ leds[i]->setColor(yellow);
+ leds[i]->off();
+ }
+ uint max = cfactory->cbi.maxGiftsToSend;
+ if ( nb>max ) {
+ uint p = nb - max;
+ nb = 0;
+ put(p);
+ return max;
+ } else {
+ ready = false;
+ uint t = nb;
+ nb = 0;
+ return t;
+ }
+}
+
+void GiftPool::reset()
+{
+ killTimers();
+ ready = false;
+ nb = 0;
+ for (uint i=0; i<leds.size(); i++) {
+ leds[i]->setColor(yellow);
+ leds[i]->off();
+ }
+}
+
+//-----------------------------------------------------------------------------
+PlayerProgress::PlayerProgress(BaseBoard *board, QWidget *parent,
+ const char *name)
+ : KGameProgress(0, board->matrix().height(), 0, KGameProgress::Vertical,
+ parent, name), _board(board)
+{
+ setBackgroundColor(lightGray);
+ setTextEnabled(false);
+ setBarColor(blue);
+ KZoomMainWindow::addWidget(this);
+}
+
+QSize PlayerProgress::sizeHint() const
+{
+ return QSize(10, _board->matrix().height() * BasePrefs::blockSize())
+ + 2 * QSize(frameWidth(), frameWidth());
+}
+
+QSizePolicy PlayerProgress::sizePolicy() const
+{
+ return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+}
diff --git a/libksirtet/common/misc_ui.h b/libksirtet/common/misc_ui.h
new file mode 100644
index 00000000..3a89acaa
--- /dev/null
+++ b/libksirtet/common/misc_ui.h
@@ -0,0 +1,81 @@
+#ifndef COMMON_MISC_UI_H
+#define COMMON_MISC_UI_H
+
+#include <qcanvas.h>
+#include <qptrvector.h>
+#include <qhbox.h>
+
+#include <kgameprogress.h>
+#include "lib/libksirtet_export.h"
+
+#include "base/board.h"
+
+/*****************************************************************************/
+class LIBKSIRTET_EXPORT ShowNextPiece : public FixedCanvasView
+{
+ Q_OBJECT
+ public:
+ ShowNextPiece(BaseBoard *, QWidget *parent);
+};
+
+/*****************************************************************************/
+class LIBKSIRTET_EXPORT Shadow : public QWidget
+{
+ Q_OBJECT
+ public:
+ Shadow(BaseBoard *, QWidget *parent);
+
+ virtual QSize sizeHint() const;
+ virtual QSizePolicy sizePolicy() const;
+ void setDisplay(bool show);
+
+ private:
+ int _xOffset;
+ const BaseBoard *_board;
+ bool _show;
+
+ void paintEvent(QPaintEvent *);
+};
+
+/*****************************************************************************/
+class Led;
+
+class LIBKSIRTET_EXPORT GiftPool : public QHBox
+{
+ Q_OBJECT
+ public:
+ GiftPool(QWidget *parent);
+
+ virtual QSize sizeHint() const;
+ virtual QSizePolicy sizePolicy() const;
+
+ void reset();
+ void put(uint);
+ uint take();
+ bool pending() const { return ready; }
+
+ private slots:
+ void timeout() { ready = true; }
+
+ private:
+ QPtrVector<Led> leds;
+ uint _timeout, nb;
+ bool ready;
+};
+
+
+/*****************************************************************************/
+class LIBKSIRTET_EXPORT PlayerProgress : public KGameProgress
+{
+ Q_OBJECT
+public:
+ PlayerProgress(BaseBoard *board, QWidget *parent = 0, const char *name = 0);
+
+ virtual QSize sizeHint() const;
+ virtual QSizePolicy sizePolicy() const;
+
+private:
+ BaseBoard *_board;
+};
+
+#endif
diff --git a/libksirtet/common/settings.cpp b/libksirtet/common/settings.cpp
new file mode 100644
index 00000000..902d1e56
--- /dev/null
+++ b/libksirtet/common/settings.cpp
@@ -0,0 +1,54 @@
+#include "settings.h"
+#include "settings.moc"
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qcheckbox.h>
+#include <qwhatsthis.h>
+
+#include <klocale.h>
+#include <knuminput.h>
+#include <kdialog.h>
+#include <kglobal.h>
+
+
+//-----------------------------------------------------------------------------
+AppearanceConfig::AppearanceConfig()
+{
+ int row = _grid->numRows();
+ int col = _grid->numCols();
+
+ QCheckBox *cb = new QCheckBox(i18n("Show piece's shadow"), _main, "kcfg_ShowPieceShadow");
+ _grid->addMultiCellWidget(cb, row, row, 0, col-1);
+
+ cb = new QCheckBox(i18n("Show next piece"), _main, "kcfg_ShowNextPiece");
+ _grid->addMultiCellWidget(cb, row+1, row+1, 0, col-1);
+
+ cb = new QCheckBox(i18n("Show detailed \"removed lines\" field"), _main, "kcfg_ShowDetailedRemoved");
+ _grid->addMultiCellWidget(cb, row+2, row+2, 0, col-1);
+}
+
+//-----------------------------------------------------------------------------
+GameConfig::GameConfig()
+ : QWidget(0, "game config")
+{
+ QVBoxLayout *top = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint());
+
+ _grid = new QGridLayout(top, 3, 2);
+ _grid->setColStretch(1, 1);
+
+ QLabel *label = new QLabel(i18n("Initial level:"), this);
+ _grid->addWidget(label, 0, 0);
+ KIntNumInput *in = new KIntNumInput(this, "kcfg_InitialGameLevel");
+ in->setRange(1, 20, 1, true);
+ _grid->addWidget(in, 0, 1);
+
+ _grid->addRowSpacing(1, KDialog::spacingHint());
+
+ QCheckBox *cb = new QCheckBox(i18n("Direct drop down"), this, "kcfg_DirectDropDownEnabled");
+ QWhatsThis::add(cb, i18n("Drop down is not stopped when drop down key is released."));
+ _grid->addMultiCellWidget(cb, 2, 2, 0, 1);
+
+ top->addStretch(1);
+}
+
diff --git a/libksirtet/common/settings.h b/libksirtet/common/settings.h
new file mode 100644
index 00000000..ceda13a1
--- /dev/null
+++ b/libksirtet/common/settings.h
@@ -0,0 +1,28 @@
+#ifndef COMMON_SETTINGS_H
+#define COMMON_SETTINGS_H
+
+#include "base/settings.h"
+
+#include "lib/libksirtet_export.h"
+
+
+//-----------------------------------------------------------------------------
+class LIBKSIRTET_EXPORT AppearanceConfig : public BaseAppearanceConfig
+{
+ Q_OBJECT
+public:
+ AppearanceConfig();
+};
+
+//-----------------------------------------------------------------------------
+class LIBKSIRTET_EXPORT GameConfig : public QWidget
+{
+ Q_OBJECT
+public:
+ GameConfig();
+
+protected:
+ QGridLayout *_grid;
+};
+
+#endif
diff --git a/libksirtet/common/types.cpp b/libksirtet/common/types.cpp
new file mode 100644
index 00000000..05d91db3
--- /dev/null
+++ b/libksirtet/common/types.cpp
@@ -0,0 +1,16 @@
+#include "types.h"
+
+LIBKSIRTET_EXPORT QDataStream &operator <<(QDataStream &s, const ClientPlayData &d)
+ { s << d.height << d.gift << d.end; return s; }
+LIBKSIRTET_EXPORT QDataStream &operator >>(QDataStream &s, ClientPlayData &d)
+ { s >> d.height >> d.gift >> d.end; return s; }
+
+LIBKSIRTET_EXPORT QDataStream &operator <<(QDataStream &s, const ServerPlayData &d)
+ { s << d.prevHeight << d.nextHeight << d.gift; return s; }
+LIBKSIRTET_EXPORT QDataStream &operator >>(QDataStream &s, ServerPlayData &d)
+ { s >> d.prevHeight >> d.nextHeight >> d.gift; return s; }
+
+LIBKSIRTET_EXPORT QDataStream &operator <<(QDataStream &s, const ServerInitData &d)
+{ s << d.initLevel << d.seed << d.nextName << d.prevName << d.name; return s; }
+LIBKSIRTET_EXPORT QDataStream &operator >>(QDataStream &s, ServerInitData &d)
+{ s >> d.initLevel >> d.seed >> d.nextName >> d.prevName >> d.name; return s; }
diff --git a/libksirtet/common/types.h b/libksirtet/common/types.h
new file mode 100644
index 00000000..8a30d276
--- /dev/null
+++ b/libksirtet/common/types.h
@@ -0,0 +1,26 @@
+#ifndef COMMON_TYPES_H
+#define COMMON_TYPES_H
+
+#include <qdatastream.h>
+
+#include "lib/libksirtet_export.h"
+
+
+struct ClientPlayData { Q_UINT8 height, gift, end; };
+LIBKSIRTET_EXPORT QDataStream &operator <<(QDataStream &s, const ClientPlayData &d);
+LIBKSIRTET_EXPORT QDataStream &operator >>(QDataStream &s, ClientPlayData &d);
+
+struct ServerPlayData { Q_UINT8 prevHeight, nextHeight, gift; };
+LIBKSIRTET_EXPORT QDataStream &operator <<(QDataStream &s, const ServerPlayData &d);
+LIBKSIRTET_EXPORT QDataStream &operator >>(QDataStream &s, ServerPlayData &d);
+
+class ServerInitData
+{
+ public:
+ QString prevName, nextName, name;
+ Q_UINT32 initLevel, seed;
+};
+LIBKSIRTET_EXPORT QDataStream &operator <<(QDataStream &s, const ServerInitData &d);
+LIBKSIRTET_EXPORT QDataStream &operator >>(QDataStream &s, ServerInitData &d);
+
+#endif
diff --git a/libksirtet/configure.in.in b/libksirtet/configure.in.in
new file mode 100644
index 00000000..fef48401
--- /dev/null
+++ b/libksirtet/configure.in.in
@@ -0,0 +1,5 @@
+AC_CHECK_HEADERS(fcntl.h sys/time.h unistd.h sys/select.h sys/filio.h)
+
+dnl libksirtet is completely not prepared for visibility support, check for it being enabled
+dnl so we can disable it in the Makefile.am.
+AM_CONDITIONAL(disable_VISIBILITY, test "$HAVE_GCC_VISIBILITY" = "1")
diff --git a/libksirtet/lib/CHANGELOG b/libksirtet/lib/CHANGELOG
new file mode 100644
index 00000000..84c03edb
--- /dev/null
+++ b/libksirtet/lib/CHANGELOG
@@ -0,0 +1,39 @@
+0.1.8 (11 April 2001)
+ * better Player/Meeting "choose boxes"
+ * use KExtendedSocket (get rid of custom utilities)
+ and hopefully fix network game
+
+0.1.7
+ * resize handle removed from statusbar of netmeeting dialog
+
+0.1.6
+ * fixed a bug in key configuration
+ * players name access improved
+ * some internal cleanups + use of KDialogBase
+
+0.1.5
+ * many bug fixes for network game
+ * added a framework for simple multiplayers games
+ * Wizard-like configuration
+ * enhanced keys configuration
+
+0.1.4
+ * all data transport is now done via QDataStream (no more bitorder/storage
+ problems).
+ * QDataStream use make things easier from the user point of view (at least
+ I think so).
+ * big cleaning : the library restricts itself to data transport between
+ boards and to game configuration. The library doesn't want to and doesn't
+ have to manage things like game pause or gameover of a specific player ...
+ all those things must be done by the game programmer.
+
+0.1.3
+ * ported to QT 2.0 (hard way : now we send QString over the network :)
+
+0.1.2
+ * finally THE bug has been found (eight months later) !
+ so network game seems stable.
+ * lots of bug fixes
+
+0.1.1
+ * first code (June 1998 : pause in dvplt)
diff --git a/libksirtet/lib/LICENSE b/libksirtet/lib/LICENSE
new file mode 100644
index 00000000..6afe98e6
--- /dev/null
+++ b/libksirtet/lib/LICENSE
@@ -0,0 +1,18 @@
+kdemultiplayers library
+-----------------------
+Copyright (c) 1998-2001 Nicolas HADACEK (hadacek@kde.org)
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Library 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 Library General Public License for more details.
+
+You should have received a copy of the GNU Library 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.
diff --git a/libksirtet/lib/Makefile.am b/libksirtet/lib/Makefile.am
new file mode 100644
index 00000000..890ac458
--- /dev/null
+++ b/libksirtet/lib/Makefile.am
@@ -0,0 +1,21 @@
+INCLUDES = $(all_includes)
+
+# Don't compile with hidden symbols since we are a library.
+if disable_VISIBILITY
+KDE_CXXFLAGS = -fvisibility=default
+endif
+
+noinst_LTLIBRARIES = libksirtetmultiplayers.la
+
+noinst_HEADERS = defines.h types.h miscui.h wizard.h pline.h meeting.h \
+ socket.h smanager.h internal.h keys.h mp_board.h mp_option.h \
+ mp_interface.h mp_simple_types.h mp_simple_board.h \
+ mp_simple_interface.h
+
+libksirtetmultiplayers_la_SOURCES = miscui.cpp types.cpp defines.cpp \
+ socket.cpp smanager.cpp pline.cpp \
+ wizard.cpp \
+ meeting.cpp keys.cpp mp_interface.cpp internal.cpp \
+ mp_simple_types.cpp mp_simple_board.cpp mp_simple_interface.cpp
+
+METASOURCES = AUTO
diff --git a/libksirtet/lib/README b/libksirtet/lib/README
new file mode 100644
index 00000000..442c9c30
--- /dev/null
+++ b/libksirtet/lib/README
@@ -0,0 +1,63 @@
+kdemultiplayers library
+-----------------------
+Copyright (c) 1998-2000 Nicolas HADACEK (hadacek@kde.org)
+Distributed under the GNU Library General Public License
+
+Introduction
+------------
+The "kdemultiplayers" library is an attempt to address the realization of
+multiplayer games localy on a computer (the players have each a "board" on
+the screen and they use the same keyboard) or/and by network (between several
+computers). The implementation is not so simple but this library gives
+a basic API which allows a lot of flexibility.
+
+Note: The headers which defines the API have names beginning with "mp".
+
+The library provides three services :
+ * a set of dialog widgets to allow the players to configure/create/join
+ a game.
+ * management of all the sending/receiving data between
+ player boards so you as a game programmer will *not* have to deal with
+ sockets or network programming.
+ * a framework for simple multiplayers game (for instance ksirtet) with a
+ very simple API ("mp_simple" headers).
+
+Semantics
+---------
+"player" : an individual which take part in the game (it can also be an AI).
+"AI" : artificial intelligence (a game that is played by a program).
+"board" : there is one board per player ; it is the widget where the player
+ acts in the game.
+"host" : a computer which hosts one or several players.
+"server" : the host that has created the game.
+"client" : a host that is not the server.
+
+
+Basic description of the API
+----------------------------
+From the game programmer point of view there should be no difference between
+a local multiplayers game (one host with several players) and a network game
+(several hosts with one or more players).
+
+Each player has a board which is a widget inherited from the class "LocalBoard"
+(see "mp_board.h").
+
+On each host there must be a class inherited from "MPInterface"
+which manages local boards and the data transport with other hosts.
+(see "mp_interface.h").
+
+During the game at given intervals of time, the server asks data from clients
+and from its local boards. After treating this data, the server dispatches back
+results to all boards.
+
+General advice
+--------------
+There should be no blocking operation or long computation as it will freeze
+the user front-end and will also block the multiplayers data exchanges
+which operate by timer signals.
+
+There are general rules to avoid such blocking :
+ * answer to X/QT events such as keyboard/mouse/window events with
+ short methods.
+ * use timer(s) to make things evolve in time or to break long
+ computation in shorter parts.
diff --git a/libksirtet/lib/TODO b/libksirtet/lib/TODO
new file mode 100644
index 00000000..54a924bc
--- /dev/null
+++ b/libksirtet/lib/TODO
@@ -0,0 +1,18 @@
+* change to an event-driven data exchange framework (it currently uses a timer
+ on the server side) -> we probably need a way to ensure clients are not
+ dead (?)
+
+* grid/row layout of boards
+* better dialogs ...
+* check the 64bit fix in "types.h"
+
+* user help (add help button to wizard)
+* tooltips !!
+* API documentation
+
+* heavy test of network game
+* test option widget in netmeeting (--> needs fixing in NetMeeting class)
+* test of options for players and computers
+
+* keys configuration : probably need to change KKeyDialog (the msgbox for
+ duplicate keys is so annoying ...) + unused key finder
diff --git a/libksirtet/lib/defines.cpp b/libksirtet/lib/defines.cpp
new file mode 100644
index 00000000..7c897dd9
--- /dev/null
+++ b/libksirtet/lib/defines.cpp
@@ -0,0 +1,24 @@
+#include "defines.h"
+
+#include <klocale.h>
+
+void errorBox(const QString &msg1, const QString &msg2, QWidget *parent)
+{
+ QString str;
+ if ( msg2.isNull() ) str = msg1;
+ else str = i18n("%1:\n%2").arg(msg1).arg(msg2);
+ KMessageBox::error(parent, str);
+}
+
+QString socketError(const KExtendedSocket *socket)
+{
+ return KExtendedSocket::strError(socket->status(), socket->systemError());
+}
+
+bool checkSocket(int res, const KExtendedSocket *socket,
+ const QString &msg, QWidget *parent)
+{
+ if ( res==0 ) return false;
+ errorBox(msg, socketError(socket), parent);
+ return true;
+}
diff --git a/libksirtet/lib/defines.h b/libksirtet/lib/defines.h
new file mode 100644
index 00000000..9015c6b1
--- /dev/null
+++ b/libksirtet/lib/defines.h
@@ -0,0 +1,35 @@
+#ifndef DEFINES_H
+#define DEFINES_H
+
+#include <kmessagebox.h>
+#include <kextsock.h>
+
+// constants
+#define TALKER_MAX_LENGTH 35
+#define NAME_MAX_LENGTH 15
+
+// config keys
+#define MP_GROUP "Multi-Players"
+#define MP_GAMETYPE "Game type"
+#define MP_PLAYER_NAME "Player name #%1"
+#define MP_PLAYER_TYPE "Player type #%1"
+#define MP_SERVER_ADDRESS "Server address"
+#define MP_PORT "Port"
+
+void errorBox(const QString &msg1, const QString &msg2, QWidget *parent);
+QString socketError(const KExtendedSocket *socket);
+bool checkSocket(int res, const KExtendedSocket *,
+ const QString &msg, QWidget *parent);
+
+#define R_ERROR_BOX(msg1, msg2) { \
+ errorBox(msg1, msg2, this); \
+ return; \
+}
+
+template <class Type>
+bool XOR(Type a, Type b)
+{
+ return ( (!a && b) || (a && !b) );
+}
+
+#endif // DEFINES_H
diff --git a/libksirtet/lib/internal.cpp b/libksirtet/lib/internal.cpp
new file mode 100644
index 00000000..c7b69f9e
--- /dev/null
+++ b/libksirtet/lib/internal.cpp
@@ -0,0 +1,278 @@
+#include "internal.h"
+#include "internal.moc"
+
+#include <qptrlist.h>
+#include <klocale.h>
+#include "mp_interface.h"
+
+#define DATA_ERROR(i) { dataError(i); return; }
+#define READ_ERROR(i) { readError(i); return; }
+#define WRITE_ERROR(i) { writeError(i); return; }
+#define BROKE_ERROR(i) { brokeError(i); return; }
+#define LAG_ERROR { lagError(); return; }
+
+
+//-----------------------------------------------------------------------------
+void Local::dataError(uint i)
+{
+ qWarning("MP : Invalid data from board #%i", i);
+}
+
+void Local::readData(bool inverse)
+{
+ // read data from local boards
+ for (uint i=0; i<ios.size(); i++) {
+ boards[i].ptr->dataOut(ios[i]->writing);
+ if (inverse) ios[i]->writingToReading();
+ }
+}
+
+void Local::writeData(bool inverse)
+{
+ // write data to local boards
+ for (uint i=0; i<ios.size(); i++) {
+ if (inverse) ios[i]->writingToReading();
+ boards[i].ptr->dataIn(ios[i]->reading);
+ if ( !ios[i]->reading.readOk() ) DATA_ERROR(i);
+ }
+}
+
+void Local::treatData()
+{
+ readData(TRUE);
+ interface->treatData();
+ // check reading stream
+ for (uint i=0; i<ios.size(); i++)
+ if ( !ios[i]->reading.readOk() ) DATA_ERROR(i);
+ writeData(TRUE);
+}
+
+//-----------------------------------------------------------------------------
+Server::Server(uint _interval)
+: interval(_interval)
+{
+ timer.start(interval);
+}
+
+void Server::congestion()
+{
+ qWarning("MP : congestion occurred !!");
+}
+
+void Server::serverTimeout()
+{
+ ctimer.start(2*interval, TRUE);
+ timeout();
+}
+
+//-----------------------------------------------------------------------------
+Network::Network(MPInterface *_interface,
+ QValueList<MPInterface::Data> &_boards,
+ const QPtrList<RemoteHostData> &rhd)
+: Local(_interface, _boards)
+{
+ RemoteData rd;
+ QPtrListIterator<RemoteHostData> it(rhd);
+ for (; it.current(); ++it) {
+ rd.socket = it.current()->socket;
+ rd.socket->notifier()->setEnabled(TRUE);
+ connect(rd.socket->notifier(), SIGNAL(activated(int)),
+ SLOT(notifier(int)));
+ uint nb = it.current()->bds.count();
+ Q_ASSERT( nb>=1 );
+ rd.array = new BufferArray(nb);
+ for (uint k=0; k<it.current()->bds.count(); k++)
+ rd.names += it.current()->bds[k].name;
+ remotes += rd;
+ }
+}
+
+Network::~Network()
+{
+ for (uint i=0; i<remotes.count(); i++) {
+ delete remotes[i].socket;
+ delete remotes[i].array;
+ }
+}
+
+uint Network::nbPlayers() const
+{
+ uint nb = Local::nbPlayers();
+ for (uint i=0; i<remotes.count(); i++) nb += remotes[i].array->size();
+ return nb;
+}
+
+QString Network::playerName(uint i) const
+{
+ uint l = Local::nbPlayers();
+ if ( i<l ) return Local::playerName(i);
+ for (uint k=0; k<remotes.count(); k++) {
+ uint ll = remotes[k].array->size();
+ if ( i<(l+ll) ) return remotes[k].names[i-l];
+ l += ll;
+ }
+ return QString::null; // dummy
+}
+
+IOBuffer *Network::ioBuffer(uint i) const
+{
+ if ( i<Local::nbPlayers() ) return Local::ioBuffer(i);
+ i -= Local::nbPlayers();
+ for (uint k=0; k<remotes.count(); k++) {
+ if ( i<remotes[k].array->size() ) return (*remotes[k].array)[i];
+ i -= remotes[k].array->size();
+ }
+ Q_ASSERT(FALSE);
+ return 0;
+}
+
+void Network::readError(uint i)
+{
+ disconnectHost(i, i18n("Unable to read socket"));
+}
+
+void Network::writeError(uint i)
+{
+ disconnectHost(i, i18n("Unable to write to socket"));
+}
+
+void Network::brokeError(uint i)
+{
+ disconnectHost(i, i18n("Link broken"));
+}
+
+void Network::disconnectHost(uint i, const QString &msg)
+{
+ delete remotes[i].socket;
+ delete remotes[i].array;
+ remotes.remove(remotes.at(i));
+ interface->hostDisconnected(i, msg);
+}
+
+//-----------------------------------------------------------------------------
+LocalServer::LocalServer(MPInterface *_interface,
+ QValueList<MPInterface::Data> &_boards,
+ uint _interval)
+: Local(_interface, _boards), Server(_interval)
+{
+ connect(&timer, SIGNAL(timeout()), SLOT(timeoutSlot()));
+ connect(&ctimer, SIGNAL(timeout()), SLOT(congestionTimeoutSlot()));
+ serverTimeout();
+}
+
+//-----------------------------------------------------------------------------
+NetworkServer::NetworkServer(MPInterface *_interface,
+ QValueList<MPInterface::Data> &_boards,
+ const QPtrList<RemoteHostData> &rhd, uint _interval)
+: Network(_interface, _boards, rhd), Server(_interval),
+ nbReceived(remotes.count())
+{
+ connect(&timer, SIGNAL(timeout()), SLOT(timeoutSlot()));
+ connect(&ctimer, SIGNAL(timeout()), SLOT(congestionTimeoutSlot()));
+ // to catch unexpected data
+ for (uint i=0; i<remotes.count(); i++) remotes[i].received = TRUE;
+ nbReceived = remotes.count();
+// let the client the time to create itself ...
+// serverTimeout();
+}
+
+void NetworkServer::timeout()
+{
+ if ( nbReceived<remotes.count() ) LAG_ERROR;
+ nbReceived = 0;
+ for (uint i=0; i<remotes.count(); i++) remotes[i].received = FALSE;
+ // send MF_ASK : asking for data from clients
+ for (uint i=0; i<remotes.count(); i++) {
+ remotes[i].socket->writingStream() << MF_Ask;
+// debug("SERVER : send ask flag");
+ if ( !remotes[i].socket->write() ) WRITE_ERROR(i);
+ }
+}
+
+void NetworkServer::notifier(int fd)
+{
+ uint i;
+ for (i=0; i<remotes.count(); i++)
+ if ( remotes[i].socket->fd()==fd ) break;
+ Q_ASSERT( i<remotes.count() );
+
+ if ( remotes[i].received ) READ_ERROR(i);
+ switch ( remotes[i].socket->read() ) {
+ case -1: READ_ERROR(i);
+ case 0: BROKE_ERROR(i);
+ }
+
+ remotes[i].received = TRUE;
+ nbReceived++;
+
+ ReadingStream &s = remotes[i].socket->readingStream();
+// debug("SERVER : notifier + read (fd=%i i=%i size=%i)", fd, i,
+// s.size());
+ s >> *remotes[i].array;
+ if ( !s.readOk() ) DATA_ERROR(i);
+
+ // all data from clients received
+ if ( nbReceived==remotes.count() ) treatData();
+}
+
+void NetworkServer::writeData(bool inverse)
+{
+ Local::writeData(inverse);
+ for (uint i=0; i<remotes.count(); i++) {
+ WritingStream &s = remotes[i].socket->writingStream();
+ s << MF_Data;
+ s << *remotes[i].array;
+ s.writeRawBytes(globalStream()->buffer().data(),
+ globalStream()->size());
+// debug("SERVER : write data (size= 1 + %i + %i=%i)",
+// globalStream()->size(), s.size()-globalStream()->size()-1,
+// s.size());
+ if ( !remotes[i].socket->write() ) WRITE_ERROR(i);
+ }
+ globalStream()->clear();
+}
+
+void NetworkServer::lagError()
+{
+ for (uint i=0; i<remotes.count(); i++)
+ if ( !remotes[i].received )
+ disconnectHost(i, i18n("Client has not answered in time"));
+}
+
+//-----------------------------------------------------------------------------
+void Client::notifier(int)
+{
+ switch ( remotes[0].socket->read() ) {
+ case -1: READ_ERROR(0);
+ case 0: BROKE_ERROR(0);
+ }
+ ReadingStream &s = remotes[0].socket->readingStream();
+ MetaFlag mf;
+ s >> mf;
+ if ( !s.readOk() ) DATA_ERROR(0);
+// debug("CLIENT : reading stream (size=%i flag=%i)", s.size(),
+// (int)mf);
+ switch(mf) {
+ case MF_Ask:
+ // write data from local boards to server socket (cleaning
+ // of writing stream is done in write())
+ readData(FALSE);
+ remotes[0].socket->writingStream() << ios;
+// debug("CLIENT : send ios (size=%i)",
+// remotes[0].socket->writingStream().size());
+ if ( !remotes[0].socket->write() ) WRITE_ERROR(0);
+ break;
+ case MF_Data:
+ // read data from server to interface & local boards
+// debug("CLIENT : before receive ios (at=%i)", s.device()->at());
+ s >> ios;
+// debug("CLIENT : after receive ios (at=%i)", s.device()->at());
+ interface->dataFromServer(s);
+// debug("CLIENT : after dataFromServer (at=%i)", s.device()->at());
+ if ( !s.readOk() ) DATA_ERROR(0);
+ writeData(FALSE);
+ break;
+ default: DATA_ERROR(0);
+ }
+ if ( !s.atEnd() ) qWarning("CLIENT : remaining data");
+}
diff --git a/libksirtet/lib/internal.h b/libksirtet/lib/internal.h
new file mode 100644
index 00000000..4dece16a
--- /dev/null
+++ b/libksirtet/lib/internal.h
@@ -0,0 +1,152 @@
+#ifndef INTERNAL_H
+#define INTERNAL_H
+
+#include <qtimer.h>
+#include <qstringlist.h>
+
+#include "socket.h"
+#include "mp_interface.h"
+
+class MPBoard;
+class RemoteHostData;
+
+//-----------------------------------------------------------------------------
+class Local
+{
+ public:
+ Local(MPInterface *_interface, QValueList<MPInterface::Data> &_boards)
+ : interface(_interface), ios(_boards.count()), boards(_boards) {}
+ virtual ~Local() {}
+
+ virtual uint nbPlayers() const { return boards.count(); }
+ virtual QString playerName(uint i) const { return boards[i].name; }
+ virtual IOBuffer *ioBuffer(uint i) const { return ios[i]; }
+ virtual void writeData(bool inverse);
+ virtual WritingStream *globalStream() { return 0; }
+
+ protected:
+ MPInterface *interface;
+ BufferArray ios;
+
+ void dataError(uint i);
+ void readData(bool inverse);
+ void treatData();
+
+ private:
+ QValueList<MPInterface::Data> boards;
+};
+
+//-----------------------------------------------------------------------------
+class Server
+{
+ public:
+ Server(uint _interval);
+ virtual ~Server() {}
+
+ protected:
+ WritingStream stream;
+ QTimer timer, ctimer;
+
+ virtual void timeout() = 0;
+ void serverTimeout();
+ void congestion();
+
+ private:
+ uint interval;
+};
+
+//-----------------------------------------------------------------------------
+class Network : public QObject, public Local
+{
+ Q_OBJECT
+
+ public:
+ Network(MPInterface *_interface, QValueList<MPInterface::Data> &_boards,
+ const QPtrList<RemoteHostData> &rhd);
+ virtual ~Network();
+
+ virtual uint nbPlayers() const;
+ QString playerName(uint i) const;
+ IOBuffer *ioBuffer(uint i) const;
+
+ protected slots:
+ virtual void notifier(int fd) = 0;
+
+ protected:
+ class RemoteData {
+ public:
+ RemoteData() {}
+ Socket *socket;
+ BufferArray *array;
+ bool received;
+ QStringList names;
+ };
+ QValueList<RemoteData> remotes;
+
+ void readError(uint i);
+ void writeError(uint i);
+ void brokeError(uint i);
+ void disconnectHost(uint i, const QString &msg);
+};
+
+//-----------------------------------------------------------------------------
+class LocalServer : public QObject, public Local, public Server
+{
+ Q_OBJECT
+
+ public:
+ LocalServer(MPInterface *_interface,
+ QValueList<MPInterface::Data> &_boards, uint _interval);
+
+ WritingStream *globalStream() { return &stream; }
+
+ private slots:
+ void timeoutSlot() { serverTimeout(); }
+ void congestionTimeoutSlot() { congestion(); }
+
+ private:
+ void timeout() { treatData(); }
+};
+
+//-----------------------------------------------------------------------------
+class NetworkServer : public Network, public Server
+{
+ Q_OBJECT
+
+ public:
+ NetworkServer(MPInterface *_interface,
+ QValueList<MPInterface::Data> &_boards,
+ const QPtrList<RemoteHostData> &rhd, uint _interval);
+
+ void writeData(bool inverse);
+ WritingStream *globalStream() { return &stream; }
+
+ private slots:
+ void timeoutSlot() { serverTimeout(); }
+ void congestionTimeoutSlot() { congestion(); }
+ void notifier(int fd);
+
+ private:
+ uint nbReceived;
+
+ void lagError();
+ void timeout();
+};
+
+//-----------------------------------------------------------------------------
+class Client : public Network
+{
+ Q_OBJECT
+
+ public:
+ Client(MPInterface *_interface, QValueList<MPInterface::Data> &_boards,
+ const QPtrList<RemoteHostData> &rhd)
+ : Network(_interface, _boards, rhd) {}
+
+ uint nbPlayers() const { return Local::nbPlayers(); }
+
+ private slots:
+ void notifier(int fd);
+};
+
+#endif // INTERNAL_H
diff --git a/libksirtet/lib/keys.cpp b/libksirtet/lib/keys.cpp
new file mode 100644
index 00000000..879f0bfa
--- /dev/null
+++ b/libksirtet/lib/keys.cpp
@@ -0,0 +1,104 @@
+#include "keys.h"
+#include "keys.moc"
+
+#include <qsignal.h>
+#include <kkeydialog.h>
+#include <klocale.h>
+
+
+KeyData::KeyData(uint maxNb, uint nbActions, const ActionData *data,
+ QObject *parent)
+ : QObject(parent), _maxNb(maxNb)
+{
+ _data.duplicate(data, nbActions);
+
+ for (uint n=0; n<maxNb; n++)
+ for (uint i=0; i<=n; i++)
+ _keycodes[n][i].fill(0, nbActions);
+}
+
+void KeyData::setKeycodes(uint nb, uint index, const int *keycodes)
+{
+ Q_ASSERT( nb!=0 && nb-1<_maxNb && index<nb );
+ for (uint n=nb-1; n<_maxNb; n++)
+ for (uint k=0; k<_data.size(); k++)
+ if ( n==nb-1 || _keycodes[n][index][k]==0 )
+ _keycodes[n][index][k] = keycodes[k];
+}
+
+void KeyData::setCurrentNb(uint nb)
+{
+ Q_ASSERT( nb<_maxNb );
+ clear();
+ _cols.fill(0, nb);
+}
+
+void KeyData::clear()
+{
+ for (uint i=0; i<_cols.size(); i++)
+ delete _cols[i];
+ _cols.resize(0);
+}
+
+void KeyData::createActionCollection(uint index, QWidget *receiver)
+{
+ Q_ASSERT( index<_cols.size() );
+ _cols[index] = new KActionCollection(receiver, this);
+ for (uint k=0; k<_data.size(); k++) {
+ QString label = i18n(_data[k].label);
+ QString name = QString("%2 %3").arg(index+1).arg(_data[k].name);
+ const char *slot = (_data[k].slotRelease ? 0 : _data[k].slot);
+ KAction *a = new KAction(label, _keycodes[_cols.size()-1][index][k],
+ receiver, slot, _cols[index], name.utf8());
+ a->setEnabled(false);
+ if ( slot==0 ) {
+ SpecialData data;
+ data.enabled = false;
+ data.pressed = new QSignal(this);
+ data.pressed->connect(receiver, _data[k].slot);
+ data.released = new QSignal(this);
+ data.released->connect(receiver, _data[k].slotRelease);
+ _specActions[a] = data;
+ }
+ }
+ _cols[index]->readShortcutSettings(group());
+}
+
+void KeyData::setEnabled(uint index, bool enabled)
+{
+ for (uint k=0; k<_cols[index]->count(); k++) {
+ QMap<KAction *, SpecialData>::Iterator it =
+ _specActions.find(_cols[index]->action(k));
+ if ( it==_specActions.end() )
+ _cols[index]->action(k)->setEnabled(enabled);
+ else (*it).enabled = enabled;
+ }
+}
+
+void KeyData::addKeys(KKeyDialog &d)
+{
+ for (uint i=0; i<_cols.size(); i++)
+ d.insert(_cols[i], i18n("Shortcuts for player #%1/%2").arg(i+1)
+ .arg(_cols.size()));
+}
+
+void KeyData::save()
+{
+ for (uint i=0; i<_cols.size(); i++)
+ _cols[i]->writeShortcutSettings(group());
+}
+
+void KeyData::keyEvent(QKeyEvent *e, bool pressed)
+{
+ if ( e->isAutoRepeat() ) return;
+
+ KKey key(e);
+ QMap<KAction *, SpecialData>::Iterator it = _specActions.begin();
+ for(; it!=_specActions.end(); ++it) {
+ if ( !it.data().enabled ) continue;
+ if ( !it.key()->shortcut().contains(key) ) continue;
+ if (pressed) it.data().pressed->activate();
+ else it.data().released->activate();
+ }
+ e->ignore();
+}
diff --git a/libksirtet/lib/keys.h b/libksirtet/lib/keys.h
new file mode 100644
index 00000000..07c08419
--- /dev/null
+++ b/libksirtet/lib/keys.h
@@ -0,0 +1,42 @@
+#ifndef KEYS_H
+#define KEYS_H
+
+#include <qmap.h>
+#include <kaction.h>
+
+#include "mp_interface.h"
+
+
+class KeyData : public QObject
+{
+ Q_OBJECT
+ public:
+ KeyData(uint maxNb, uint nbActions, const ActionData *,
+ QObject *parent);
+ void setKeycodes(uint nb, uint i, const int *keycodes);
+
+ void setCurrentNb(uint nb);
+ void clear();
+ void createActionCollection(uint index, QWidget *receiver);
+ void setEnabled(uint index, bool enabled);
+ void addKeys(KKeyDialog &);
+ void save();
+
+ void keyEvent(QKeyEvent *e, bool pressed);
+
+ private:
+ uint _maxNb;
+ QMemArray<ActionData> _data;
+ QMap<int, QMap<int, QMemArray<int> > > _keycodes;
+ QMemArray<KActionCollection *> _cols;
+ struct SpecialData {
+ bool enabled;
+ QSignal *pressed, *released;
+ };
+ QMap<KAction *, SpecialData> _specActions;
+
+ QString group() const
+ { return QString("Keys (%1 humans)").arg(_cols.size()); }
+};
+
+#endif // KEYS_H
diff --git a/libksirtet/lib/libksirtet_export.h b/libksirtet/lib/libksirtet_export.h
new file mode 100644
index 00000000..819e0029
--- /dev/null
+++ b/libksirtet/lib/libksirtet_export.h
@@ -0,0 +1,35 @@
+/*
+ This file is part of libkexif project
+ Copyright (c) 2005 Laurent Montel <montel@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _LIBKEXIF_EXPORT_H
+#define _LIBKEXIF_EXPORT_H
+
+#ifdef KDEMACROS_USABLE
+#include <kdemacros.h>
+#endif
+
+#ifdef KDE_EXPORT
+#define LIBKSIRTET_EXPORT KDE_EXPORT
+#else
+#define LIBKSIRTET_EXPORT
+#endif
+
+#endif /* _LIBKEXIF_EXPORT_H */
+
diff --git a/libksirtet/lib/meeting.cpp b/libksirtet/lib/meeting.cpp
new file mode 100644
index 00000000..2cb6e285
--- /dev/null
+++ b/libksirtet/lib/meeting.cpp
@@ -0,0 +1,575 @@
+#include "meeting.h"
+
+#include <qmessagebox.h>
+#include <qpushbutton.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include "defines.h"
+#include "mp_option.h"
+
+#define LIST_INTERVAL 0
+
+
+NetMeeting::NetMeeting(const cId &_id, Socket *socket,
+ MPOptionWidget *option,
+ bool _server, QWidget *parent, const char * name)
+: KDialogBase(Plain, i18n("Network Meeting"),
+ (_server ? Ok|Cancel|Help : Cancel|Help),
+ (_server ? Ok : Cancel), parent, name),
+ server(_server), ow(option), id(_id), socketRemoved(FALSE)
+{
+ sm.append(socket, SocketManager::ReadWrite);
+ sm[0]->notifier()->setEnabled(TRUE);
+
+/* top layout */
+ QVBoxLayout *top = new QVBoxLayout(plainPage(), spacingHint());
+ top->setResizeMode(QLayout::Fixed);
+
+ // server line
+ spl = new MeetingLine(server, server, true, plainPage());
+ top->addWidget(spl);
+
+ // widget list
+ wl = new WidgetList<MeetingLine>(LIST_INTERVAL, plainPage());
+ wl->hide();
+ top->addWidget(wl);
+
+ labWait = new QLabel(i18n("Waiting for clients"), plainPage());
+ labWait->setAlignment(AlignCenter);
+ top->addWidget(labWait);
+
+ // options widget
+// if (ow) top->addWidget(ow); #### FIXME
+
+ // status bar
+ status = new QStatusBar(plainPage());
+ status->setSizeGripEnabled(false);
+ top->addWidget(status);
+
+ // buttons
+ enableButtonSeparator(TRUE);
+ if (server) {
+ setButtonOK(i18n("Start Game"));
+ enableButtonOK(FALSE);
+ }
+ setButtonCancel(server ? i18n("Abort") : i18n("Quit"));
+ enableButton(Help, FALSE);
+}
+
+NetMeeting::~NetMeeting()
+{}
+
+void NetMeeting::appendLine(const MeetingLineData &pld, bool server)
+{
+ MeetingLine *pl;
+ pl = new MeetingLine(pld.own, server, false, wl);
+ if (pld.own) connect(pl, SIGNAL(textChanged(const QString &)),
+ SLOT(textChanged(const QString &)));
+ else message(i18n("A new client has just arrived (#%1)")
+ .arg(wl->size()+1));
+ pl->setData(pld.ed);
+ connect(pl, SIGNAL(typeChanged(MeetingCheckBox::Type)),
+ SLOT(typeChanged(MeetingCheckBox::Type)));
+ wl->append(pl);
+ waiting();
+}
+
+void NetMeeting::removeLine(uint i)
+{
+ wl->remove(i);
+ waiting();
+}
+
+void NetMeeting::waiting()
+{
+ if ( wl->size() ) {
+ labWait->hide();
+ wl->show();
+ } else {
+ labWait->show();
+ wl->hide();
+ }
+ if (server) enableButtonOK(ready());
+}
+
+void NetMeeting::setType(const TypeInfo &ti)
+{
+ if ( ti.i==0 ) spl->setType(ti.type); // in fact should not append
+ else {
+ wl->widget(ti.i-1)->setType(ti.type);
+ if (server) enableButtonOK(ready());
+ }
+}
+
+void NetMeeting::setText(const TextInfo &ti)
+{
+ if ( ti.i==0 ) spl->setText(ti.text);
+ else wl->widget(ti.i-1)->setText(ti.text);
+}
+
+bool NetMeeting::ready() const
+{
+ int nbReady = 0;
+ for(uint k=0; k<wl->size(); k++) {
+ switch ( wl->widget(k)->type() ) {
+ case MeetingCheckBox::Ready : nbReady++; break;
+ case MeetingCheckBox::NotReady : return FALSE;
+ default : break;
+ }
+ }
+ return ( nbReady!=0 );
+}
+
+void NetMeeting::cleanReject(const QString &str)
+{
+ sm.clean(); // remove the sockets immediately to avoid possible further mess
+ if ( !str.isEmpty() )
+ KMessageBox::information(this, str, caption());
+ KDialogBase::reject();
+}
+
+#define WRITE(i) if ( !sm[i]->write() ) { writeError(i); return; }
+#define CHECK_READ(i) if ( !sm[i]->readingStream().readOk() ) { dataError(i); return; }
+
+// Read incoming data
+void NetMeeting::readNotifier(int fd)
+{
+ int i = sm.find(fd);
+ Q_ASSERT( i!=-1 );
+ switch ( sm[i]->read() ) {
+ case -1: readError(i); break;
+ case 0: brokeError(i); break;
+ default: readData(i);
+ }
+}
+
+void NetMeeting::readData(uint i)
+{
+ // get message type
+ MeetingMsgFlag mt;
+ sm[i]->readingStream() >> mt;
+ CHECK_READ(i);
+ switch (mt) {
+ case EndFlag: endFlag(i); break;
+ case NewFlag: newFlag(i); break;
+ case Mod_TextFlag: modTextFlag(i); break;
+ case Mod_TypeFlag: modTypeFlag(i); break;
+ case IdFlag: idFlag(i); break;
+ case DelFlag: delFlag(i); break;
+ case Mod_OptFlag: modOptFlag(i); break;
+ case PlayFlag: playFlag(i); break;
+ default: dataError(i);
+ }
+
+ if (socketRemoved) socketRemoved = FALSE;
+ else if ( !sm[i]->readingStream().atEnd() )
+ readData(i); // more pending data
+}
+
+void NetMeeting::readError(uint i)
+ { netError(i, i18n("Error reading data from")); }
+void NetMeeting::dataError(uint i)
+ { netError(i, i18n("Unknown data from")); }
+void NetMeeting::writeError(uint i)
+ { netError(i, i18n("Error writing to")); }
+void NetMeeting::brokeError(uint i)
+ { netError(i, i18n("Link broken or empty data from")); }
+
+bool NetMeeting::checkState(uint i, PlayerState s)
+{
+ bool ok = ( players[i]==s );
+ if (!ok) dataError(i);
+ return ok;
+}
+
+bool NetMeeting::checkAndSetState(uint i, PlayerState os, PlayerState ns)
+{
+ bool ok = checkState(i, os);
+ if (ok) players[i] = ns;
+ return ok;
+}
+
+void NetMeeting::reject()
+{
+ // send an End flag
+ sm.commonWritingStream() << EndFlag;
+ writeToAll();
+
+ cleanReject();
+}
+
+void NetMeeting::accept()
+{
+ KDialogBase::accept();
+}
+
+void NetMeeting::message(const QString &str)
+{
+ status->message(str, 3000);
+}
+
+/** ServerNetMeeting *********************************************************/
+ServerNetMeeting::ServerNetMeeting(const cId &id,
+ const RemoteHostData &r, MPOptionWidget *option,
+ QPtrList<RemoteHostData> &arhd, QWidget *parent, const char * name)
+: NetMeeting(id, r.socket, option, TRUE, parent, name), rhd(arhd)
+{
+ connect(sm[0]->notifier(), SIGNAL(activated(int)), SLOT(newHost(int)));
+ players.append(Accepted); // server
+
+ // set server line
+ ExtData ed(r.bds, "", MeetingCheckBox::Ready);
+ spl->setData(ed);
+ connect(spl, SIGNAL(textChanged(const QString &)),
+ SLOT(textChanged(const QString &)));
+
+ // options signal
+ if (ow) connect(ow, SIGNAL(changed()), SLOT(optionsChanged()));
+}
+
+void ServerNetMeeting::writeToAll(uint i)
+{
+ for (uint k=1; k<sm.size(); k++) {
+ if ( k==i ) continue;
+ if ( !sm.writeCommon(k) ) writeError(k);
+ }
+ sm.commonWritingStream().clear();
+}
+
+void ServerNetMeeting::netError(uint i, const QString &type)
+{
+ Q_ASSERT( i!=0 );
+ disconnectHost(i, i18n("%1 client #%2: disconnect it").arg(type).arg(i));
+}
+
+void ServerNetMeeting::disconnectHost(uint i, const QString &str)
+{
+ sm.remove(i, true);
+ socketRemoved = TRUE;
+ if ( players[i]==Accepted ) {
+ removeLine(i-1);
+
+ // Send a Del message to all (other) clients
+ sm.commonWritingStream() << DelFlag << i;
+ writeToAll();
+ }
+ players.remove(players.at(i));
+ message(str);
+}
+
+void ServerNetMeeting::newHost(int)
+{
+ KExtendedSocket *s;
+ int res = sm[0]->accept(s);
+ if ( res!=0 ) {
+ message(i18n("Failed to accept incoming client:\n%1")
+ .arg(socketError(s)));
+ return;
+ }
+ players.append(NewPlayer);
+ Socket *socket = new Socket(s, true);
+ uint i = sm.append(socket, SocketManager::ReadWrite);
+ connect(sm[i]->notifier(), SIGNAL(activated(int)),
+ SLOT(readNotifier(int)));
+ sm[i]->notifier()->setEnabled(TRUE);
+}
+
+void ServerNetMeeting::idFlag(uint i)
+{
+ bool b = checkAndSetState(i, NewPlayer, IdChecked);
+ Q_ASSERT(b);
+
+ // get client id
+ cId clientId;
+ sm[i]->readingStream() >> clientId;
+ CHECK_READ(i);
+
+ // compare id
+ id.check(clientId);
+
+ // send result to client
+ Stream &s = sm[i]->writingStream();
+ s << IdFlag << id;
+ WRITE(i);
+
+ // if not accepted : remove socket and player from list
+ if ( !id.accepted() )
+ disconnectHost(i, i18n("Client rejected for incompatible ID"));
+}
+
+void ServerNetMeeting::endFlag(uint i)
+{
+ disconnectHost(i, i18n("Client #%1 has left").arg(i));
+}
+
+void ServerNetMeeting::newFlag(uint i)
+{
+ checkAndSetState(i, IdChecked, Accepted);
+
+ // get line infos from new client (GameData struct)
+ MeetingLineData pld;
+ sm[i]->readingStream() >> pld.ed.bds;
+ CHECK_READ(i);
+
+ // complete the MeetingLineData struct with initial values
+ pld.own = FALSE; // client line
+ pld.ed.type = MeetingCheckBox::NotReady; // not ready by default
+ pld.ed.text = ""; // empty line to begin with
+ appendLine(pld, TRUE);
+
+ // send to the new client already present lines including its own
+ // (New flag + MeetingLineData struct)
+ spl->data(pld.ed);
+ sm[i]->writingStream() << NewFlag << pld.ed;
+ for(uint k=1; k<sm.size(); k++) {
+ wl->widget(k-1)->data(pld.ed);
+ pld.own = ( k==i );
+ sm[i]->writingStream() << NewFlag << pld;
+ }
+ WRITE(i);
+
+ // send to all other clients the new line (New flag + MeetingLineData struct)
+ wl->widget(i-1)->data(pld.ed);
+ pld.own = FALSE;
+ sm.commonWritingStream() << NewFlag << pld;
+ writeToAll(i);
+}
+
+void ServerNetMeeting::modTextFlag(uint i)
+{
+ checkState(i-1, Accepted);
+
+ // the client i has just sent a new text (QString)
+ TextInfo ti;
+ sm[i]->readingStream() >> ti.text;
+ CHECK_READ(i);
+ ti.i = i;
+ setText(ti);
+
+ // send it to all other clients (Mod_Text flag + TextInfo struct)
+ sm.commonWritingStream() << Mod_TextFlag << ti;
+ writeToAll(i);
+}
+
+void ServerNetMeeting::modTypeFlag(uint i)
+{
+ checkState(i-1, Accepted);
+
+ // a client has just sent a new TCB type (TCB type)
+ TypeInfo ti;
+ sm[i]->readingStream() >> ti.type;
+ CHECK_READ(i);
+ ti.i = i;
+ setType(ti);
+
+ // send it to all other clients (Mod_Type flag + TypeInfo struct)
+ sm.commonWritingStream() << Mod_TypeFlag << ti;
+ writeToAll(i);
+}
+
+void ServerNetMeeting::textChanged(const QString &text)
+{
+ // server line text changed : send to every clients (Mod_Text flag + TextInfo struct)
+ TextInfo ti; ti.i = 0; ti.text = text;
+ sm.commonWritingStream() << Mod_TextFlag << ti;
+ writeToAll();
+}
+
+void ServerNetMeeting::typeChanged(MeetingCheckBox::Type type)
+{
+ Q_ASSERT( sender()!=spl ); // server TCB not modifiable
+ // the server has changed a client TCB
+
+ // find the changed TCB index
+ TypeInfo ty;
+ ty.type = type;
+ for (ty.i=0; ty.i<wl->size(); ty.i++)
+ if ( sender()==wl->widget(ty.i) ) break;
+ ty.i++;
+
+ // TCB change : send to every clients (Mod_Type flag + TypeInfo struct)
+ sm.commonWritingStream() << Mod_TypeFlag << ty;
+ writeToAll();
+ if (server) enableButtonOK(ready());
+}
+
+void ServerNetMeeting::accept()
+{
+ Q_ASSERT( ready() && rhd.count()==0 );
+
+ // stop receiving data from clients (will be buffered by OS)
+ for (uint k=0; k<sm.size(); k++) disconnect(sm[k]->notifier());
+ sm.remove(0, true);
+
+ // check which client will play and fill RemoteHostData array
+ ExtData ed;
+ bool willPlay;
+ for (uint k=1; k<players.count(); k++) {
+ willPlay = FALSE;
+
+ if ( players[k]==Accepted ) { // client with lines
+ wl->widget(k-1)->data(ed);
+ if ( ed.type==MeetingCheckBox::Ready ) {
+ willPlay = TRUE;
+ RemoteHostData *r = new RemoteHostData;
+ r->socket = sm[0];
+ r->bds = ed.bds;
+ rhd.append(r);
+ }
+
+ // send play message to client (Play flag
+ // + bool [accepted/rejected])
+ sm[0]->writingStream() << PlayFlag << (Q_UINT8)willPlay;
+ // if write failed and the client is not playing : silently
+ // put it aside ...
+ if ( !sm[0]->write() && willPlay ) {
+ cleanReject(i18n("Unable to write to client #%1 at game "
+ "beginning."));
+ return;
+ }
+ }
+
+ sm[0]->notifier()->setEnabled(false);
+ sm.remove(0, !willPlay);
+ }
+
+ NetMeeting::accept();
+}
+
+void ServerNetMeeting::optionsChanged()
+{
+ sm.commonWritingStream() << Mod_OptFlag;
+ ow->dataOut( sm.commonWritingStream() );
+ writeToAll();
+}
+
+/** ClientNetMeeting *********************************************************/
+ClientNetMeeting::ClientNetMeeting(const cId &id,
+ const RemoteHostData &rhd, MPOptionWidget *option,
+ QWidget *parent, const char * name)
+: NetMeeting(id, rhd.socket, option, FALSE, parent, name), bds(rhd.bds)
+{
+ connect(sm[0]->notifier(), SIGNAL(activated(int)),
+ SLOT(readNotifier(int)));
+ players.append(NewPlayer); // server player
+
+ // Send id to server (Id flag + Id struct)
+ sm.commonWritingStream() << IdFlag << id;
+ writeToAll(); // what happens if there is a message box appearing before exec() call ??
+}
+
+void ClientNetMeeting::netError(uint, const QString &str)
+{
+ cleanReject(i18n("%1 server: aborting connection.").arg(str));
+}
+
+void ClientNetMeeting::writeToAll(uint)
+{
+ if ( !sm.writeCommon(0) ) writeError(0);
+ sm.commonWritingStream().clear();
+}
+
+void ClientNetMeeting::idFlag(uint)
+{
+ checkAndSetState(0, NewPlayer, IdChecked);
+
+ // read Id result (Id flag + Id struct)
+ cId serverId;
+ sm[0]->readingStream() >> serverId;
+ CHECK_READ(0);
+
+ // check result
+ if ( !serverId.accepted() ) cleanReject(serverId.errorMessage(id));
+ else {
+ // send client info (New flag + GameData struct)
+ sm.commonWritingStream() << NewFlag << bds;
+ writeToAll();
+ }
+}
+
+void ClientNetMeeting::newFlag(uint)
+{
+ if ( players[0]==IdChecked ) {
+ ExtData ed;
+ sm[0]->readingStream() >> ed;
+ spl->setData(ed);
+ players[0] = Accepted;
+ } else {
+ MeetingLineData pld;
+ sm[0]->readingStream() >> pld;
+ appendLine(pld, FALSE);
+ }
+ CHECK_READ(0);
+}
+
+void ClientNetMeeting::modTextFlag(uint)
+{
+ // receive new text from server (TextInfo struct)
+ TextInfo ti;
+ sm[0]->readingStream() >> ti;
+ CHECK_READ(0);
+ setText(ti);
+}
+
+void ClientNetMeeting::modTypeFlag(uint)
+{
+ // receive new type from server (TypeInfo struct)
+ TypeInfo ti;
+ sm[0]->readingStream() >> ti;
+ CHECK_READ(0);
+ setType(ti);
+}
+
+void ClientNetMeeting::delFlag(uint)
+{
+ // receive client number (uint)
+ uint k;
+ sm[0]->readingStream() >> k;
+ CHECK_READ(0);
+ removeLine(k-1);
+ message(i18n("Client %1 has left").arg(k));
+}
+
+void ClientNetMeeting::textChanged(const QString &text)
+{
+ // text changed : send to server (Mod_Text flag + QString)
+ sm.commonWritingStream() << Mod_TextFlag << text;
+ writeToAll();
+}
+
+void ClientNetMeeting::typeChanged(MeetingCheckBox::Type type)
+{
+ // type changed : send to server (Mod_Type flag + TCB)
+ sm.commonWritingStream() << Mod_TypeFlag << type;
+ writeToAll();
+}
+
+void ClientNetMeeting::playFlag(uint)
+{
+ // receive accept or reject (bool)
+ Q_UINT8 i;
+ sm[0]->readingStream() >> i;
+ CHECK_READ(0);
+ sm[0]->notifier()->setEnabled(false);
+ sm.remove(0, i==0);
+ socketRemoved = true;
+ if (i) accept();
+ else cleanReject(i18n("The game has begun without you\n"
+ "(You have been excluded by the server)."));
+}
+
+void ClientNetMeeting::modOptFlag(uint)
+{
+ // read new option data
+ ow->dataIn( sm[0]->readingStream() );
+ CHECK_READ(0);
+}
+
+void ClientNetMeeting::endFlag(uint)
+{
+ // abort from server
+ cleanReject(i18n("The server has aborted the game."));
+}
+#include "meeting.moc"
diff --git a/libksirtet/lib/meeting.h b/libksirtet/lib/meeting.h
new file mode 100644
index 00000000..cb534404
--- /dev/null
+++ b/libksirtet/lib/meeting.h
@@ -0,0 +1,137 @@
+#ifndef MEETING_H
+#define MEETING_H
+
+#include <qstatusbar.h>
+#include <kdialogbase.h>
+#include "smanager.h"
+#include "pline.h"
+#include "types.h"
+
+class MPOptionWidget;
+
+/** Internal class : net meeting. */
+class NetMeeting : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ // "gameName" and "gameId" are QByteArray because they are
+ // used for ID comparing between games.
+ NetMeeting(const cId &id, Socket *, MPOptionWidget *option, bool server,
+ QWidget *parent = 0, const char * name = 0);
+ virtual ~NetMeeting();
+
+ protected slots:
+ void readNotifier(int socket);
+ virtual void textChanged(const QString &) = 0;
+ virtual void typeChanged(MeetingCheckBox::Type) = 0;
+ virtual void reject();
+ virtual void accept();
+
+ protected:
+ enum PlayerState { NewPlayer, IdChecked, Accepted };
+ QValueList<PlayerState> players;
+ bool server;
+ MeetingLine *spl;
+ WidgetList<MeetingLine> *wl;
+ SocketManager sm;
+ MPOptionWidget *ow;
+ cId id;
+ bool socketRemoved;
+
+ void appendLine(const MeetingLineData &pld, bool server);
+ void removeLine(uint i);
+ void setType(const TypeInfo &ti);
+ void setText(const TextInfo &ti);
+
+ void cleanReject(const QString &str = QString::null);
+ bool checkState(uint i, PlayerState s);
+ bool checkAndSetState(uint i, PlayerState os, PlayerState ns);
+ bool ready() const;
+
+ virtual void idFlag(uint i) { dataError(i); }
+ virtual void newFlag(uint i) { dataError(i); }
+ virtual void endFlag(uint i) { dataError(i); }
+ virtual void modTypeFlag(uint i) { dataError(i); }
+ virtual void modTextFlag(uint i) { dataError(i); }
+ virtual void delFlag(uint i) { dataError(i); }
+ virtual void modOptFlag(uint i) { dataError(i); }
+ virtual void playFlag(uint i) { dataError(i); }
+
+ virtual void netError(uint i, const QString &str) = 0;
+ virtual void writeToAll(uint i=0) = 0;
+ void readError(uint i);
+ void writeError(uint i);
+ void dataError(uint i);
+ void brokeError(uint i);
+ void message(const QString &str);
+
+ private:
+ QLabel *labWait;
+ QStatusBar *status;
+
+ void waiting();
+ void readData(uint i);
+};
+
+class ServerNetMeeting : public NetMeeting
+{
+ Q_OBJECT
+
+ public:
+ ServerNetMeeting(const cId &id,
+ const RemoteHostData &rhd, MPOptionWidget *options,
+ QPtrList<RemoteHostData> &arhd,
+ QWidget *parent = 0, const char * name = 0);
+
+ private slots:
+ void newHost(int);
+ void textChanged(const QString &text);
+ void typeChanged(MeetingCheckBox::Type);
+ void accept();
+ void optionsChanged();
+
+ private:
+ QPtrList<RemoteHostData> &rhd;
+
+ void idFlag(uint i);
+ void newFlag(uint i);
+ void endFlag(uint i);
+ void modTypeFlag(uint i);
+ void modTextFlag(uint i);
+
+ void netError(uint i, const QString &str);
+ void writeToAll(uint i = 0);
+ void disconnectHost(uint i, const QString &str);
+};
+
+class ClientNetMeeting : public NetMeeting
+{
+ Q_OBJECT
+
+ public:
+ ClientNetMeeting(const cId &id,
+ const RemoteHostData &rhd, MPOptionWidget *options,
+ QWidget *parent = 0, const char * name = 0);
+
+ private slots:
+ void textChanged(const QString &text);
+ void typeChanged(MeetingCheckBox::Type);
+
+ private:
+ QValueList<BoardData> bds;
+
+ void idFlag(uint);
+ void newFlag(uint);
+ void endFlag(uint);
+ void delFlag(uint);
+ void modTypeFlag(uint);
+ void modTextFlag(uint);
+ void modOptFlag(uint);
+ void playFlag(uint);
+
+ void writeToAll(uint i=0);
+ void netError(uint, const QString &str);
+};
+
+#endif // MEETING_H
diff --git a/libksirtet/lib/miscui.cpp b/libksirtet/lib/miscui.cpp
new file mode 100644
index 00000000..28f00914
--- /dev/null
+++ b/libksirtet/lib/miscui.cpp
@@ -0,0 +1,58 @@
+#include "miscui.h"
+#include "miscui.moc"
+
+#include <qlayout.h>
+
+#include <klocale.h>
+
+
+//-----------------------------------------------------------------------------
+MeetingCheckBox::MeetingCheckBox(Type type, bool owner, bool server,
+ QWidget *parent)
+ : QWidget(parent, "meeting_check_box")
+{
+ QVBoxLayout *vbox = new QVBoxLayout(this);
+
+ _ready = new QCheckBox(i18n("Ready"), this);
+ vbox->addWidget(_ready);
+ _ready->setEnabled(owner);
+ connect(_ready, SIGNAL(clicked()), SLOT(changedSlot()));
+
+ _excluded = new QCheckBox(i18n("Excluded"), this);
+ vbox->addWidget(_excluded);
+ _excluded->setEnabled(server);
+ connect(_excluded, SIGNAL(clicked()), SLOT(changedSlot()));
+
+ setType(type);
+}
+
+void MeetingCheckBox::setType(Type type)
+{
+ _ready->setChecked( type==Ready );
+ _excluded->setChecked( type==Excluded );
+}
+
+MeetingCheckBox::Type MeetingCheckBox::type() const
+{
+ if ( _excluded->isChecked() ) return Excluded;
+ if ( _ready->isChecked() ) return Ready;
+ return NotReady;
+}
+
+void MeetingCheckBox::changedSlot()
+{
+ emit changed(type());
+}
+
+//-----------------------------------------------------------------------------
+PlayerComboBox::PlayerComboBox(Type type, bool canBeEmpty, bool acceptAI,
+ QWidget *parent)
+ : QComboBox(parent, "player_combo_box")
+{
+ insertItem(i18n("Human"));
+ if (acceptAI) insertItem(i18n("AI"));
+ if (canBeEmpty) insertItem(i18n("None"));
+ setCurrentItem(type);
+
+ connect(this, SIGNAL(activated(int)), SIGNAL(changed(int)));
+}
diff --git a/libksirtet/lib/miscui.h b/libksirtet/lib/miscui.h
new file mode 100644
index 00000000..7de5dfbc
--- /dev/null
+++ b/libksirtet/lib/miscui.h
@@ -0,0 +1,43 @@
+#ifndef MISCUI_H
+#define MISCUI_H
+
+#include <qcombobox.h>
+#include <qcheckbox.h>
+
+
+//-----------------------------------------------------------------------------
+class MeetingCheckBox : public QWidget
+{
+ Q_OBJECT
+ public:
+ enum Type { Ready, NotReady, Excluded };
+ MeetingCheckBox(Type, bool owner, bool server, QWidget *parent);
+
+ void setType(Type);
+ Type type() const;
+
+ signals:
+ void changed(int);
+
+ private slots:
+ void changedSlot();
+
+ private:
+ QCheckBox *_ready, *_excluded;
+};
+
+//-----------------------------------------------------------------------------
+class PlayerComboBox : public QComboBox
+{
+ Q_OBJECT
+ public:
+ enum Type { Human = 0, AI, None };
+ PlayerComboBox(Type, bool canBeNone, bool acceptAI, QWidget *parent);
+
+ Type type() const { return (Type)currentItem(); }
+
+ signals:
+ void changed(int);
+};
+
+#endif // MISCUI_H
diff --git a/libksirtet/lib/mp_board.h b/libksirtet/lib/mp_board.h
new file mode 100644
index 00000000..fbd1e01f
--- /dev/null
+++ b/libksirtet/lib/mp_board.h
@@ -0,0 +1,51 @@
+#ifndef MP_BOARD_H
+#define MP_BOARD_H
+
+#include <qwidget.h>
+
+/**
+ * The MP_Board class is the base widget from which each individual
+ * board should inheritate ; you must implement its virtual methods.
+ */
+class MPBoard : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ MPBoard(QWidget *parent, const char *name=0)
+ : QWidget(parent, name) {}
+ virtual ~MPBoard() {}
+
+ /**
+ * This method is called once at the board creation.
+ * @param AI is TRUE if the player is not human.
+ * @param multiplayers is TRUE if the game is not a single player game.
+ * @param first is TRUE if this board is the first local one.
+ */
+ virtual void init(bool AI, bool multiplayers, bool server, bool first,
+ const QString &name) = 0;
+
+ /**
+ * Put data on the stream.
+ *
+ * This method is the communication way out. The data given here will
+ * be the only information that will go to the server.
+ */
+ virtual void dataOut(QDataStream &) = 0;
+
+ /**
+ * Get data from the stream.
+ *
+ * This method is the communication way in. The data given here will be
+ * the only information that you will receive from the server.
+ */
+ virtual void dataIn(QDataStream &) = 0;
+
+ signals:
+ /**
+ * Call this signal to enable/disable the keys associated with a board.
+ */
+ void enableKeys(bool);
+};
+
+#endif // MP_BOARD_H
diff --git a/libksirtet/lib/mp_interface.cpp b/libksirtet/lib/mp_interface.cpp
new file mode 100644
index 00000000..61742562
--- /dev/null
+++ b/libksirtet/lib/mp_interface.cpp
@@ -0,0 +1,274 @@
+#include "mp_interface.h"
+#include "mp_interface.moc"
+
+#include <qpainter.h>
+#include <qlayout.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <kkeydialog.h>
+
+#include "defines.h"
+#include "types.h"
+#include "meeting.h"
+#include "internal.h"
+#include "keys.h"
+#include "wizard.h"
+
+/*****************************************************************************/
+/* Constructor & Destructor */
+/*****************************************************************************/
+MPInterface::MPInterface(const MPGameInfo &_gameInfo,
+ uint nbActions, const ActionData *data,
+ QWidget *parent, const char *name)
+: QWidget(parent, name), internal(0), gameInfo(_gameInfo), nbLocalHumans(0)
+{
+ Q_ASSERT( gameInfo.maxNbLocalPlayers>=1 );
+
+ hbl = new QHBoxLayout(this, 0, 5);
+ hbl->setResizeMode( QLayout::Fixed );
+
+ _keyData = new KeyData(gameInfo.maxNbLocalPlayers, nbActions, data, this);
+}
+
+MPInterface::~MPInterface()
+{
+ delete internal;
+}
+
+/*****************************************************************************/
+/* Game creation */
+/*****************************************************************************/
+void MPInterface::clear()
+{
+ if (internal) {
+ stop();
+ delete internal;
+ internal = 0;
+ _keyData->clear();
+ }
+}
+
+void MPInterface::dialog()
+{
+ clear();
+
+ // configuration wizard
+ ConnectionData cd;
+ MPWizard wiz(gameInfo, cd, this);
+ if ( wiz.exec()==QDialog::Rejected ) {
+ singleHuman(); // create a single game
+ return;
+ }
+
+ // net meeting
+ QPtrList<RemoteHostData> rhd;
+ rhd.setAutoDelete(TRUE);
+ if (cd.network) {
+ cId id(kapp->name(), gameInfo.gameId);
+ MPOptionWidget *ow = newOptionWidget();
+ NetMeeting *nm;
+ if (cd.server) nm = new ServerNetMeeting(id, cd.rhd, ow, rhd, this);
+ else nm = new ClientNetMeeting(id, cd.rhd, ow, this);
+ int res = nm->exec();
+ if (ow) {
+ if (res) ow->saveData();
+ delete ow;
+ }
+ delete nm;
+ if (!res) {
+ singleHuman();
+ return;
+ }
+ }
+
+ createLocalGame(cd);
+ if (cd.server) createServerGame(rhd);
+ else createClientGame(cd.rhd);
+}
+
+void MPInterface::specialLocalGame(uint nbHumans, uint nbAIs)
+{
+ clear();
+
+ ConnectionData cd;
+ BoardData bd;
+ PlayerComboBox::Type t;
+ KConfigGroupSaver cg(kapp->config(), MP_GROUP);
+ for (uint i=0; i<(nbHumans+nbAIs); i++) {
+ bd.type = (i<nbHumans ? PlayerComboBox::Human : PlayerComboBox::AI);
+ bd.name = QString::null;
+ t = (PlayerComboBox::Type)
+ cg.config()->readNumEntry(QString(MP_PLAYER_TYPE).arg(i),
+ PlayerComboBox::None);
+ if ( bd.type==t )
+ bd.name = cg.config()->readEntry(QString(MP_PLAYER_NAME).arg(i),
+ QString::null);
+ if ( bd.name.isNull() )
+ bd.name = (i<nbHumans ? i18n("Human %1").arg(i+1)
+ : i18n("AI %1").arg(i-nbHumans+1));
+ cd.rhd.bds += bd;
+ }
+ cd.server = TRUE;
+ cd.network = FALSE;
+ Q_ASSERT( (nbHumans+nbAIs)<=gameInfo.maxNbLocalPlayers );
+ Q_ASSERT( gameInfo.AIAllowed || nbAIs==0 );
+
+ createLocalGame(cd);
+ QPtrList<RemoteHostData> rhd;
+ createServerGame(rhd);
+}
+
+void MPInterface::createServerGame(const QPtrList<RemoteHostData> &rhd)
+{
+ internal = (rhd.count()
+ ? (Local *)new NetworkServer(this, boards, rhd, gameInfo.interval)
+ : (Local *)new LocalServer(this, boards, gameInfo.interval));
+ init();
+}
+
+void MPInterface::createClientGame(const RemoteHostData &rhd)
+{
+ QPtrList<RemoteHostData> r;
+ r.append((RemoteHostData *)&rhd);
+ internal = new Client(this, boards, r);
+ init();
+}
+
+void MPInterface::createLocalGame(const ConnectionData &cd)
+{
+ _server = cd.server;
+ nbLocalHumans = 0;
+ for (uint i=0; i<cd.rhd.bds.count(); i++)
+ if ( cd.rhd.bds[i].type==PlayerComboBox::Human ) nbLocalHumans++;
+
+ // add or remove boards
+ uint old_s = boards.count();
+ uint new_s = cd.rhd.bds.count();
+ for (uint i=new_s; i<old_s; i++) {
+ delete boards[i].ptr;
+ boards.remove(boards.at(i));
+ }
+ Data d;
+ for(uint i=old_s; i<new_s; i++) {
+ d.ptr = newBoard(i);
+ hbl->addWidget(d.ptr);
+ d.ptr->show();
+ connect(d.ptr, SIGNAL(enableKeys(bool)), SLOT(enableKeys(bool)));
+ boards += d;
+ }
+
+ // init local boards
+ _keyData->setCurrentNb(nbLocalHumans);
+ int k = 0;
+ for (uint i=0; i<boards.count(); i++) {
+ bool h = ( cd.rhd.bds[i].type==PlayerComboBox::Human );
+ boards[i].humanIndex = (h ? k : -1);
+ if (h) {
+ _keyData->createActionCollection(k, boards[i].ptr);
+ k++;
+ }
+ boards[i].name = cd.rhd.bds[i].name;
+ boards[i].ptr->init(!h, cd.network || boards.count()>1, _server, i==0,
+ cd.rhd.bds[i].name);
+ }
+}
+
+/*****************************************************************************/
+/* Key management */
+/*****************************************************************************/
+void MPInterface::setDefaultKeycodes(uint nb, uint i, const int *def)
+{
+ _keyData->setKeycodes(nb, i, def);
+}
+
+void MPInterface::addKeys(KKeyDialog &d)
+{
+ _keyData->addKeys(d);
+}
+
+void MPInterface::saveKeys()
+{
+ _keyData->save();
+}
+
+void MPInterface::enableKeys(bool enable)
+{
+ if ( nbLocalHumans==0 ) return;
+ // find the sending board
+ uint i;
+ for (i=0; i<boards.count(); i++) if ( sender()==boards[i].ptr ) break;
+ int hi = boards[i].humanIndex;
+ if ( hi==-1 ) return; // AI board (no keys)
+ _keyData->setEnabled(hi, enable);
+}
+
+void MPInterface::keyPressEvent(QKeyEvent *e)
+{
+ _keyData->keyEvent(e, true);
+}
+
+void MPInterface::keyReleaseEvent(QKeyEvent *e)
+{
+ _keyData->keyEvent(e, false);
+}
+
+/*****************************************************************************/
+/* Misc. functions */
+/*****************************************************************************/
+uint MPInterface::nbPlayers() const
+{
+ return internal->nbPlayers();
+}
+
+QString MPInterface::playerName(uint i) const
+{
+ Q_ASSERT(_server);
+ return internal->playerName(i);
+}
+
+QDataStream &MPInterface::readingStream(uint i) const
+{
+ Q_ASSERT(_server);
+ return internal->ioBuffer(i)->reading;
+}
+
+QDataStream &MPInterface::writingStream(uint i) const
+{
+ return internal->ioBuffer(i)->writing;
+}
+
+QDataStream &MPInterface::dataToClientsStream() const
+{
+ Q_ASSERT(_server);
+ return *internal->globalStream();
+}
+
+void MPInterface::immediateWrite()
+{
+ internal->writeData(_server);
+}
+
+void MPInterface::hostDisconnected(uint, const QString &msg)
+{
+ errorBox(msg, QString::null, this);
+
+ if ( !disconnected ) { // to avoid multiple calls
+ disconnected = TRUE;
+ // the zero timer is used to be outside the "internal" class
+ QTimer::singleShot(0, this, SLOT(singleHumanSlot()));
+ }
+}
+
+void MPInterface::singleHumanSlot()
+{
+ disconnected = FALSE;
+ singleHuman();
+}
+
+void MPInterface::paintEvent(QPaintEvent *e)
+{
+ QPainter p(this);
+ p.fillRect(e->rect(), darkGray);
+}
diff --git a/libksirtet/lib/mp_interface.h b/libksirtet/lib/mp_interface.h
new file mode 100644
index 00000000..ad925cba
--- /dev/null
+++ b/libksirtet/lib/mp_interface.h
@@ -0,0 +1,246 @@
+#ifndef MP_INTERFACE_H
+#define MP_INTERFACE_H
+
+#include <qwidget.h>
+#include <qvaluelist.h>
+#include <qptrlist.h>
+
+#include "mp_board.h"
+#include "mp_option.h"
+
+class QHBoxLayout;
+class Local;
+class ConnectionData;
+class RemoteHostData;
+class KeyData;
+class KeyCollection;
+class KKeyDialog;
+class KAction;
+
+struct ActionData {
+ const char *label, *name;
+ const char *slot, *slotRelease; // if slotRelease!=0
+ // : use keyPress/ReleaseEvent mecanism
+};
+
+/**
+ * This structure contains information about the game
+ * configuration.
+ */
+typedef struct {
+ /** The game version id used for identification (e.g. "4").
+ * You should change this id when the game is made incompatible
+ * with previous version. (changes in data for example).
+ */
+ const char *gameId;
+
+ /** Maximum number of local players. */
+ uint maxNbLocalPlayers;
+
+ /** Interval (in msec.) between data exchange. */
+ uint interval;
+
+ /** If there are built-in artificial intelligences that can play. */
+ bool AIAllowed;
+
+ /** Slot for player/AI additional configuration. These must be SLOTs which
+ * take an "int" as parameter. It must open a setting
+ * dialog for the corresponding local player/computer and save the
+ * new settings in the config file. It should probably create a group
+ * with the given number in its name.
+ * If such a pointer is set to 0 : it means there is no perticular
+ * setting.
+ */
+ const char *humanSettingSlot, *AISettingSlot;
+} MPGameInfo;
+
+/**
+ * The MPInterface class is useful for multiplayers game
+ * management. Each game is represented by a class you have inherited
+ * from the @ref MPBoard class.
+ *
+ * Multiplayers games can take place with several (humans or eventually
+ * AIs) players on the same computer (they use the same keyboard and have
+ * each a @ref MPBoard widget on the screen) or/and network players.
+ *
+ * This class is intended to do all the hard work of sending/receiving data
+ * between the players and to send the keyboard events to the right
+ * @ref MPBoard. So multiplayers game should be completely transparent
+ * from your point of view.
+ *
+ * Note : The data exchange is done in background with a timer calling at given
+ * intervals the read/write methods. Obviously this kind of things can be done
+ * easily with threads but I have no experience with thread programming
+ * and not all people have thread libraries and a thread-safe system.
+ */
+class MPInterface : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ /** Constructor which takes a MPGameInfo struct as parameter.
+ */
+ MPInterface(const MPGameInfo &gameInfo,
+ uint nbActions, const ActionData *data,
+ QWidget *parent = 0, const char *name = 0);
+ virtual ~MPInterface();
+
+ public slots:
+ /** Create a single player game for a human being.
+ * Call @ref stop if a game is already created. */
+ void singleHuman() { specialLocalGame(1, 0); }
+ /** Create a local game opposing two human beings.
+ * Call @ref stop if a game is already created. */
+ void humanVsHuman() { specialLocalGame(2, 0); }
+ /** Create a local game opposing a human with an AI.
+ * Call @ref stop if a game is already created. */
+ void humanVsComputer() { specialLocalGame(1, 1); }
+
+ /** Open a dialog to create a multiplayer game.
+ * Call @ref stop if a game is already created. */
+ void dialog();
+
+ public:
+ virtual void addKeys(KKeyDialog &);
+ void saveKeys();
+
+ /** Called when a new game is created. At this point
+ * the number of players is known. */
+ virtual void init() {}
+
+ /** Called just before a new game is created (called by
+ * singleHuman, humanVsHuman, humanVsComputer and dialog). */
+ virtual void stop() {}
+
+ /** Called when the start button of the netmeeting is pressed. */
+ virtual void start() {}
+
+ /**
+ * Set keys configuration for the given number of human players.
+ * The size of the array is the number of defined actions.
+ */
+ void setDefaultKeycodes(uint nbHumans, uint human, const int *keycodes);
+
+ /**
+ * @return the total number of players.
+ * (If called from client : return the number of local boards).
+ */
+ uint nbPlayers() const;
+
+ /**
+ * @return true if the interface is the server.
+ */
+ bool server() const { return _server; }
+
+ /** @return the player name.
+ Do not call from client !
+ */
+ QString playerName(uint i) const;
+
+ /**
+ * Create a new @ref MPBoard.
+ *
+ * @param i is the game index that goes from 0 to the number of
+ * local players : it can be used to retrieve configuration settings.
+ */
+ virtual MPBoard *newBoard(uint i) = 0;
+
+ /**
+ * This method must read data from each client with method
+ * @ref readingStream, do the needed treatement
+ * (for instance which players has lost, which data to be resent, ...) and
+ * then write the useful data to each client with method
+ * @ref writingStream.
+ *
+ * NB: this method is also called for single player games but
+ * you probably only want to check for game over condition (it depends
+ * on game implementation that you really return data to the board).
+ */
+ virtual void treatData() = 0;
+
+ /** @return the reading stream for board #i.
+ * Do not call from client !
+ */
+ QDataStream &readingStream(uint i) const;
+
+ /** @return the writing stream for board #i.
+ */
+ QDataStream &writingStream(uint i) const;
+
+ /**
+ * Read data sent from server to clients "MultiplayersInterface"
+ * (this data is not addressed to boards).
+ * These are meta data that are not directly used in game.
+ * It can be used to display "game over" infos for all
+ * local games.
+ * NB: the use of this method is optional.
+ */
+ virtual void dataFromServer(QDataStream &) {}
+
+ /** Used by the server to write meta data to clients.
+ * NB: the use of this method is optional.
+ * Do not call from client !
+ */
+ QDataStream &dataToClientsStream() const;
+
+ /** Write immediately data to clients and local boards.
+ * It is unlike the normal exchange which is driven
+ * by the timer of the server. Be aware of possible
+ * interactions.
+ */
+ void immediateWrite();
+
+ /**
+ * This method should be overload if an option widget is used in the
+ * the "netmeeting" dialog). By default a
+ * null pointer is returned and so no option widget is shown.
+ * The option widget must be inherited from the @ref MPOptionWidget class.
+ */
+ virtual MPOptionWidget *newOptionWidget() const { return 0; }
+
+ /** Called when a network error occurred or when a host gets disconnected.
+ * The default implementation displays a message and calls singleHumans()
+ * ie it stops the current game. By overloading this method, it is
+ * possible to continue the game at this point with the remaining players.
+ */
+ virtual void hostDisconnected(uint i, const QString &msg);
+
+ protected:
+ void paintEvent(QPaintEvent *);
+ void keyPressEvent(QKeyEvent *);
+ void keyReleaseEvent(QKeyEvent *);
+
+ private slots:
+ void enableKeys(bool enable);
+ void singleHumanSlot();
+
+ public:
+ class Data {
+ public:
+ Data() {}
+ MPBoard *ptr;
+ int humanIndex;
+ QString name;
+ };
+
+ private:
+ Local *internal;
+ const MPGameInfo gameInfo;
+ QValueList<Data> boards;
+ uint nbLocalHumans;
+ QHBoxLayout *hbl;
+ bool _server, disconnected;
+
+ KeyData *_keyData;
+ QMemArray<KeyCollection *> _keyCol;
+
+ void createServerGame(const QPtrList<RemoteHostData> &);
+ void createClientGame(const RemoteHostData &);
+ void createLocalGame(const ConnectionData &);
+ void specialLocalGame(uint nbHumans, uint nbComputers);
+
+ void clear();
+ void initKeys(uint nbHumans);
+};
+
+#endif // MP_INTERFACE_H
diff --git a/libksirtet/lib/mp_option.h b/libksirtet/lib/mp_option.h
new file mode 100644
index 00000000..94c59107
--- /dev/null
+++ b/libksirtet/lib/mp_option.h
@@ -0,0 +1,74 @@
+#ifndef MP_OPTION_H
+#define MP_OPTION_H
+
+#include <qwidget.h>
+
+/**
+ * The OptionWidget is a base widget for the option widget in the
+ * "netmeeting" dialog. This option widget is optional (!).
+ *
+ * For example you will have :
+ * <PRE>
+ * class MyOptionWidget : public OptionWidget { ... };
+ * class MyMultiPlayerInterface : public MultiPlayerInterface { ... };
+ *
+ * OptionWidget *MyMultiPlayerInterface::newOptionWidget(bool server) const
+ * { return new MyOptionWidget(server); };
+ * </PRE>
+ *
+ * The option widget must have two different behaviours for server and
+ * clients. The server is able to change the options but the clients are only
+ * able to see them. The library will catch the @ref #changed signal from
+ * the option widget and will send the changes to the clients. It uses the
+ * @ref #dataOut to obtain the data from the
+ * server option dialog and then on the client side, it sets the new data with
+ * the method @ref #dataIn.
+ * You must implement this three methods to have useful option widgets and be
+ * careful to emit the signal @ref #changed whenever the server widget is
+ * changed. In addition you'll need to implement the method @ref #saveData
+ * to save the configuration in the config file ; so that it will be available
+ * for the initialisation of @ref LocalBoard.
+ * It seems a good idea that the widget have the same layout
+ * on both (server and client) sides but with the inner widgets all disabled
+ * on the client side.
+ */
+class MPOptionWidget : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ MPOptionWidget(bool Server, QWidget *parent = 0, const char *name = 0)
+ : QWidget(parent, name), server(Server) {}
+ virtual ~MPOptionWidget() {}
+
+ bool isServer() const { return server; }
+
+ /**
+ * This method is used on the client side to set the data coming from
+ * the server widget.
+ */
+ virtual void dataIn(QDataStream &s) = 0;
+
+ /** This method is used on the server side to get the data. */
+ virtual void dataOut(QDataStream &s) const = 0;
+
+ /**
+ * When the game will begin (ie when the "netmeeting" is over and the
+ * server has press the "start game" button) this method will be called
+ * (for clients and server). It must save the settings in the config
+ * file.
+ */
+ virtual void saveData() = 0;
+
+ signals:
+ /**
+ * This signal must be called each time options are changed
+ * (by the server).
+ */
+ void changed();
+
+ private:
+ bool server;
+};
+
+#endif // MP_OPTION_H
diff --git a/libksirtet/lib/mp_simple_board.cpp b/libksirtet/lib/mp_simple_board.cpp
new file mode 100644
index 00000000..f032c488
--- /dev/null
+++ b/libksirtet/lib/mp_simple_board.cpp
@@ -0,0 +1,84 @@
+#include "mp_simple_board.h"
+#include "mp_simple_board.moc"
+
+
+void MPSimpleBoard::init(bool AI, bool multiplayers, bool server, bool first,
+ const QString &name)
+{
+ state = BS_Init;
+ _init(AI, multiplayers, server, first, name);
+}
+
+void MPSimpleBoard::dataIn(QDataStream &s)
+{
+ if ( s.atEnd() ) return; // no data
+
+ IO_Flag f;
+ s >> f;
+ switch ( f.value() ) {
+ case IO_Flag::Init: initFlag(s); break;
+ case IO_Flag::Play: playFlag(s); break;
+ case IO_Flag::Pause: pauseFlag(); break;
+ case IO_Flag::GameOver: gameOverFlag(); break;
+ case IO_Flag::Stop: stopFlag(); break;
+ }
+}
+
+void MPSimpleBoard::initFlag(QDataStream &s)
+{
+ state = BS_Play;
+ emit enableKeys(true);
+ _initFlag(s);
+}
+
+void MPSimpleBoard::playFlag(QDataStream &s)
+{
+ Q_ASSERT( state==BS_Play );
+ _playFlag(s);
+}
+
+void MPSimpleBoard::pauseFlag()
+{
+ Q_ASSERT( state==BS_Play || state==BS_Pause );
+ bool p = ( state==BS_Pause );
+ state = (p ? BS_Play : BS_Pause);
+ emit enableKeys(p);
+ _pauseFlag(!p);
+}
+
+void MPSimpleBoard::gameOverFlag()
+{
+ Q_ASSERT( BS_Play );
+ _stop(TRUE);
+ state = BS_Stop;
+}
+
+void MPSimpleBoard::stopFlag()
+{
+ _stop(FALSE);
+ state = BS_Standby;
+}
+
+void MPSimpleBoard::_stop(bool gameover)
+{
+ if ( state==BS_Pause ) _pauseFlag(FALSE);
+ emit enableKeys(false);
+ _stopFlag(gameover);
+}
+
+void MPSimpleBoard::dataOut(QDataStream &s)
+{
+ switch (state) {
+ case BS_Init:
+ _initDataOut(s);
+ state = BS_Standby;
+ return;
+ case BS_Play: _dataOut(s); return;
+ case BS_Stop:
+ _gameOverDataOut(s);
+ state = BS_Standby;
+ return;
+ case BS_Pause: return;
+ case BS_Standby: return;
+ }
+}
diff --git a/libksirtet/lib/mp_simple_board.h b/libksirtet/lib/mp_simple_board.h
new file mode 100644
index 00000000..2131feb2
--- /dev/null
+++ b/libksirtet/lib/mp_simple_board.h
@@ -0,0 +1,45 @@
+#ifndef MP_SIMPLE_BOARD_H
+#define MP_SIMPLE_BOARD_H
+
+#include "mp_board.h"
+#include "mp_simple_types.h"
+
+#include <kdemacros.h>
+
+class KDE_EXPORT MPSimpleBoard : public MPBoard
+{
+ Q_OBJECT
+
+ public:
+ MPSimpleBoard(QWidget *parent = 0, const char *name = 0)
+ : MPBoard(parent, name) {}
+ virtual ~MPSimpleBoard() {}
+
+ void init(bool AI, bool multiplayers, bool server, bool first,
+ const QString &name);
+ void dataOut(QDataStream &s);
+ void dataIn(QDataStream &s);
+
+ protected:
+ virtual void _init(bool AI, bool multiplayers, bool server, bool first,
+ const QString &name) = 0;
+ virtual void _initFlag(QDataStream &s) = 0;
+ virtual void _playFlag(QDataStream &s) = 0;
+ virtual void _pauseFlag(bool pause) = 0;
+ virtual void _stopFlag(bool gameover) = 0;
+ virtual void _dataOut(QDataStream &s) = 0;
+ virtual void _gameOverDataOut(QDataStream &s) = 0;
+ virtual void _initDataOut(QDataStream &s) = 0;
+
+ private:
+ BoardState state;
+
+ void initFlag(QDataStream &s);
+ void playFlag(QDataStream &s);
+ void pauseFlag();
+ void stopFlag();
+ void gameOverFlag();
+ void _stop(bool button);
+};
+
+#endif // MP_SIMPLE_BOARD_H
diff --git a/libksirtet/lib/mp_simple_interface.cpp b/libksirtet/lib/mp_simple_interface.cpp
new file mode 100644
index 00000000..e75243a6
--- /dev/null
+++ b/libksirtet/lib/mp_simple_interface.cpp
@@ -0,0 +1,152 @@
+#include "mp_simple_interface.h"
+#include "mp_simple_interface.moc"
+
+#include <kmessagebox.h>
+#include <qtimer.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <kmainwindow.h>
+
+
+#define PAUSE_ACTION \
+ ((KToggleAction *)((KMainWindow *)topLevelWidget())->action("game_pause"))
+
+MPSimpleInterface::MPSimpleInterface(const MPGameInfo &gi,
+ uint nbActions, const ActionData *data,
+ QWidget *parent, const char *name)
+: MPInterface(gi, nbActions, data, parent, name), state(SS_Standby)
+{}
+
+void MPSimpleInterface::init()
+{
+ if ( server() ) {
+ state = SS_Standby;
+ first_init = TRUE;
+ }
+ _init();
+}
+
+void MPSimpleInterface::start()
+{
+ // WARNING : multiple calls can happen here (because button
+ // hiding is delayed)
+ state = SS_Init;
+}
+
+void MPSimpleInterface::stop()
+{
+ state = SS_Standby;
+ SC_Flag f1(SC_Flag::Stop);
+ if ( server() ) dataToClientsStream() << f1;
+ IO_Flag f2(IO_Flag::Stop);
+ for (uint i=0; i<nbPlayers(); i++) writingStream(i) << f2;
+ immediateWrite();
+}
+
+void MPSimpleInterface::addKeys(KKeyDialog &d)
+{
+ if ( !isPaused() ) pause();
+ MPInterface::addKeys(d);
+}
+
+void MPSimpleInterface::pause()
+{
+ // WARNING : multiple calls can happen here (because button
+ // hiding is delayed)
+ switch (state) {
+ case SS_Play:
+ state = SS_PauseAsked;
+ break;
+ case SS_Pause:
+ state = SS_UnpauseAsked;
+ break;
+ default: break;
+ }
+}
+
+void MPSimpleInterface::dataFromServer(QDataStream &s)
+{
+ if ( s.atEnd() ) return; // no data
+
+ SC_Flag scf;
+ s >> scf;
+ switch (scf.value()) {
+ case SC_Flag::Stop:
+ KMessageBox::information(this, i18n("Server has left game!"));
+ QTimer::singleShot(0, this, SLOT(singleHuman()));
+ return;
+ case SC_Flag::GameOver:
+ _readGameOverData(s);
+ _showGameOverData();
+ return;
+ }
+}
+
+void MPSimpleInterface::treatData()
+{
+ switch (state) {
+ case SS_Init: treatInit(); break;
+ case SS_Play: treatPlay(); break;
+ case SS_Pause: break;
+ case SS_Stop: treatStop(); break;
+ case SS_Standby: break;
+ case SS_PauseAsked: treatPause(TRUE); break;
+ case SS_UnpauseAsked: treatPause(FALSE); break;
+ }
+}
+
+void MPSimpleInterface::treatInit()
+{
+ state = SS_Play;
+
+ if (first_init) {
+ _firstInit();
+ first_init = FALSE;
+ }
+
+ IO_Flag f(IO_Flag::Init);
+ for (uint i=0; i<nbPlayers(); i++) writingStream(i) << f;
+ _treatInit();
+}
+
+void MPSimpleInterface::treatPlay()
+{
+ PAUSE_ACTION->setEnabled(true);
+ PAUSE_ACTION->setChecked(false);
+
+ bool end = _readPlayData();
+ if (end) {
+ state = SS_Stop;
+ IO_Flag f(IO_Flag::GameOver);
+ for (uint i=0; i<nbPlayers(); i++) writingStream(i) << f;
+ return;
+ }
+ if ( nbPlayers()==1 ) return; // no need to send data for singleplayer game
+ IO_Flag f(IO_Flag::Play);
+ for(uint i=0; i<nbPlayers(); i++) writingStream(i) << f;
+ _sendPlayData();
+}
+
+void MPSimpleInterface::treatPause(bool pause)
+{
+ state = (pause ? SS_Pause : SS_Play);
+ IO_Flag f(IO_Flag::Pause);
+ for (uint i=0; i<nbPlayers(); i++) writingStream(i) << f;
+
+ PAUSE_ACTION->setChecked(pause);
+}
+
+void MPSimpleInterface::treatStop()
+{
+ state = SS_Standby;
+
+ // read game over data + send them to all clients
+ QDataStream &s = dataToClientsStream();
+ SC_Flag f(SC_Flag::GameOver);
+ s << f;
+ _sendGameOverData(s);
+ _showGameOverData();
+
+ PAUSE_ACTION->setEnabled(false);
+ PAUSE_ACTION->setChecked(false);
+}
diff --git a/libksirtet/lib/mp_simple_interface.h b/libksirtet/lib/mp_simple_interface.h
new file mode 100644
index 00000000..84e6f444
--- /dev/null
+++ b/libksirtet/lib/mp_simple_interface.h
@@ -0,0 +1,48 @@
+#ifndef MP_SIMPLE_INTERFACE_H
+#define MP_SIMPLE_INTERFACE_H
+
+#include "mp_interface.h"
+#include "mp_simple_types.h"
+
+class MPSimpleInterface : public MPInterface
+{
+ Q_OBJECT
+
+ public:
+ MPSimpleInterface(const MPGameInfo &gi,
+ uint nbActions, const ActionData *data,
+ QWidget *parent = 0, const char *name = 0);
+
+ bool isPaused() const { return state==SS_Pause; }
+
+ public slots:
+ void start();
+ void pause();
+ void addKeys(KKeyDialog &);
+
+ protected:
+ virtual void _init() = 0;
+ virtual void _readGameOverData(QDataStream &s) = 0;
+ virtual void _sendGameOverData(QDataStream &s) = 0;
+ virtual void _showGameOverData() = 0;
+ virtual void _firstInit() = 0;
+ virtual void _treatInit() = 0;
+ virtual bool _readPlayData() = 0;
+ virtual void _sendPlayData() = 0;
+
+ private:
+ ServerState state;
+ bool first_init;
+
+ void treatData();
+ void treatInit();
+ void treatPlay();
+ void treatPause(bool pause);
+ void treatStop();
+
+ void init();
+ void stop();
+ void dataFromServer(QDataStream &);
+};
+
+#endif // MP_SIMPLE_INTERFACE_H
diff --git a/libksirtet/lib/mp_simple_types.cpp b/libksirtet/lib/mp_simple_types.cpp
new file mode 100644
index 00000000..a470ce7c
--- /dev/null
+++ b/libksirtet/lib/mp_simple_types.cpp
@@ -0,0 +1,6 @@
+#include "mp_simple_types.h"
+
+QDataStream &operator <<(QDataStream &s, const EnumClass &ec)
+ { s << (Q_UINT8)ec.f; return s; }
+QDataStream &operator >>(QDataStream &s, EnumClass &ec)
+ { Q_UINT8 t; s >> t; ec.f = (int)t; return s; }
diff --git a/libksirtet/lib/mp_simple_types.h b/libksirtet/lib/mp_simple_types.h
new file mode 100644
index 00000000..2fd0c289
--- /dev/null
+++ b/libksirtet/lib/mp_simple_types.h
@@ -0,0 +1,36 @@
+#ifndef WF_TYPES_H
+#define WF_TYPES_H
+
+#include <qdatastream.h>
+
+class EnumClass
+{
+ public:
+ EnumClass(int _f) : f(_f) {}
+ int f;
+};
+QDataStream &operator <<(QDataStream &s, const EnumClass &f);
+QDataStream &operator >>(QDataStream &s, EnumClass &f);
+
+class IO_Flag : public EnumClass
+{
+ public:
+ enum IOF { Init = 0, Play, Pause, Stop, GameOver };
+ IO_Flag(IOF f = Init) : EnumClass(f) {}
+ IOF value() const { return (IOF)f; }
+};
+
+enum ServerState { SS_Init, SS_Play, SS_Pause, SS_Stop, SS_Standby,
+ SS_PauseAsked, SS_UnpauseAsked };
+
+class SC_Flag : public EnumClass
+{
+ public:
+ enum SC { Stop = 0, GameOver };
+ SC_Flag(SC f = Stop) : EnumClass(f) {}
+ SC value() const { return (SC)f; }
+};
+
+enum BoardState { BS_Init, BS_Play, BS_Pause, BS_Stop, BS_Standby };
+
+#endif
diff --git a/libksirtet/lib/pline.cpp b/libksirtet/lib/pline.cpp
new file mode 100644
index 00000000..41faf6ac
--- /dev/null
+++ b/libksirtet/lib/pline.cpp
@@ -0,0 +1,147 @@
+#include "pline.h"
+#include "pline.moc"
+
+#include <qfont.h>
+#include <qpushbutton.h>
+#include <klocale.h>
+#include "defines.h"
+
+#define THIN_BORDER 4
+
+MeetingLine::MeetingLine(bool isOwner, bool serverIsReader, bool serverLine,
+ QWidget *parent, const char *name)
+: QFrame(parent, name)
+{
+ setFrameStyle(Panel | (serverLine ? Raised : Plain));
+
+ // Top layout
+ hbl = new QHBoxLayout(this, THIN_BORDER + frameWidth());
+
+ /* TriCheckBox */
+ tcb = new MeetingCheckBox(MeetingCheckBox::Ready, isOwner, serverIsReader,
+ this);
+ if ( !XOR(isOwner, serverIsReader) ) tcb->setEnabled(FALSE);
+ else connect(tcb, SIGNAL(changed(int)), SLOT(_typeChanged(int)));
+ hbl->addWidget(tcb);
+
+ /* Name */
+ lname = new QLabel(" ", this);
+ lname->setAlignment(AlignCenter);
+ lname->setFrameStyle(QFrame::Panel | QFrame::Sunken);
+ lname->setLineWidth(2);
+ lname->setMidLineWidth(3);
+ QFont f = lname->font();
+ f.setBold(TRUE);
+ lname->setFont(f);
+ lname->setFixedSize(lname->fontMetrics().maxWidth()*NAME_MAX_LENGTH,
+ lname->sizeHint().height());
+ hbl->addWidget(lname);
+ hbl->addStretch(1);
+
+ // Nb humans
+ labH = new QLabel(this);
+ hbl->addWidget(labH);
+
+ // Nb AIs
+ labAI = new QLabel(this);
+ hbl->addWidget(labAI);
+
+ // talker
+ qle = new QLineEdit(this);
+ qle->setMaxLength(TALKER_MAX_LENGTH);
+ qle->setFont( QFont("fixed", 12, QFont::Bold) );
+ qle->setFixedSize(qle->fontMetrics().maxWidth()*TALKER_MAX_LENGTH,
+ qle->sizeHint().height());
+ connect(qle, SIGNAL(textChanged(const QString &)),
+ SLOT(_textChanged(const QString &)));
+ qle->setEnabled(isOwner);
+ hbl->addWidget(qle);
+}
+
+void MeetingLine::setData(const ExtData &ed)
+{
+ bds = ed.bds;
+ uint nbh = 0, nba = 0;
+ for (uint i=0; i<bds.count(); i++) {
+ if ( bds[i].type==PlayerComboBox::Human ) nbh++;
+ else if ( bds[i].type==PlayerComboBox::AI ) nba++;
+ }
+ labH->setText(i18n("Hu=%1").arg(nbh));
+ labAI->setText(i18n("AI=%1").arg(nba));
+ lname->setText(bds[0].name);
+ setType(ed.type);
+ setText(ed.text);
+}
+
+void MeetingLine::data(ExtData &ed) const
+{
+ ed.bds = bds;
+ ed.type = tcb->type();
+ ed.text = text();
+}
+
+/*****************************************************************************/
+PlayerLine::PlayerLine(PlayerComboBox::Type type, const QString &txt,
+ bool humanSetting, bool AISetting,
+ bool canBeEmpty, bool acceptAI,
+ QWidget *parent, const char *name)
+: QFrame(parent, name), hs(humanSetting), as(AISetting)
+{
+ setFrameStyle(Panel | Raised);
+
+ // Top layout
+ QHBoxLayout *hbl;
+ hbl = new QHBoxLayout(this, THIN_BORDER + frameWidth());
+
+ /* CheckBox */
+ pcb = new PlayerComboBox(type, canBeEmpty, acceptAI, this);
+ connect(pcb, SIGNAL(changed(int)), SLOT(typeChangedSlot(int)));
+ hbl->addWidget(pcb);
+
+ /* Name */
+ edit = new QLineEdit(txt, this);
+ edit->setMaxLength(NAME_MAX_LENGTH);
+ edit->setFixedSize(edit->fontMetrics().maxWidth()*(NAME_MAX_LENGTH+2),
+ edit->sizeHint().height());
+ hbl->addWidget(edit);
+
+ /* settings button */
+ setting = new QPushButton(i18n("Settings"), this);
+ connect(setting, SIGNAL(clicked()), SLOT(setSlot()));
+ hbl->addWidget(setting);
+
+ typeChangedSlot(type);
+}
+
+void PlayerLine::typeChangedSlot(int t)
+{
+ edit->setEnabled(type()!=PlayerComboBox::None);
+ setting->setEnabled( (type()==PlayerComboBox::Human && hs)
+ || (type()==PlayerComboBox::AI && as) );
+ emit typeChanged(t);
+}
+
+void PlayerLine::setSlot()
+{
+ if ( type()==PlayerComboBox::Human ) emit setHuman();
+ else emit setAI();
+}
+
+/*****************************************************************************/
+GWidgetList::GWidgetList(uint interval, QWidget *parent, const char * name)
+ : QWidget(parent, name), vbl(this, interval)
+{
+ widgets.setAutoDelete(TRUE);
+}
+
+void GWidgetList::append(QWidget *wi)
+{
+ vbl.addWidget(wi);
+ wi->show();
+ widgets.append(wi);
+}
+
+void GWidgetList::remove(uint i)
+{
+ widgets.remove(i);
+}
diff --git a/libksirtet/lib/pline.h b/libksirtet/lib/pline.h
new file mode 100644
index 00000000..2defd10a
--- /dev/null
+++ b/libksirtet/lib/pline.h
@@ -0,0 +1,112 @@
+#ifndef PLINE_H
+#define PLINE_H
+
+#include <qframe.h>
+#include <qscrollbar.h>
+#include <qlineedit.h>
+#include <qlabel.h>
+#include <qptrlist.h>
+#include <qlayout.h>
+
+#include "types.h"
+
+class QPushButton;
+
+/** Internal class : display a "player line" in netmeeting. */
+class MeetingLine : public QFrame
+{
+ Q_OBJECT
+
+ public:
+ MeetingLine(bool isOwner, bool readerIsServer, bool serverLine,
+ QWidget *parent, const char *name = 0);
+
+ MeetingCheckBox::Type type() const { return tcb->type(); }
+ void setType(MeetingCheckBox::Type type) { tcb->setType(type); }
+ void setText(const QString &text) { qle->setText(text); }
+
+ void setData(const ExtData &ed);
+ void data(ExtData &ed) const;
+ QString text() const { return qle->text(); }
+
+ signals:
+ void typeChanged(MeetingCheckBox::Type);
+ void textChanged(const QString &);
+
+ private slots:
+ void _typeChanged(int t)
+ { emit typeChanged((MeetingCheckBox::Type)t); }
+ void _textChanged(const QString &text) { emit textChanged(text); }
+
+ protected:
+ QHBoxLayout *hbl;
+
+ private:
+ MeetingCheckBox *tcb;
+ QLabel *lname, *labH, *labAI;
+ QValueList<BoardData> bds;
+ QLineEdit *qle;
+};
+
+class PlayerLine : public QFrame
+{
+ Q_OBJECT
+
+ public:
+ PlayerLine(PlayerComboBox::Type type, const QString &txt,
+ bool humanSetting, bool AISetting,
+ bool canBeEmpty, bool acceptAI,
+ QWidget *parent = 0, const char *name = 0);
+
+ PlayerComboBox::Type type() const { return pcb->type(); }
+ QString name() const { return edit->text(); }
+
+ signals:
+ void setHuman();
+ void setAI();
+ void typeChanged(int);
+
+ private slots:
+ void setSlot();
+ void typeChangedSlot(int);
+
+ private:
+ PlayerComboBox *pcb;
+ QLineEdit *edit;
+ QPushButton *setting;
+ bool hs, as;
+};
+
+/** Internal class : scrolable list of widgets. */
+class GWidgetList : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ GWidgetList(uint interval, QWidget *parent = 0, const char * name = 0);
+
+ void remove(uint i);
+ uint size() const { return widgets.count(); }
+
+ protected:
+ /** The widget must be created with this widget as parent. */
+ void append(QWidget *);
+ QWidget *widget(uint i) { return widgets.at(i); }
+
+ private:
+ QPtrList<QWidget> widgets;
+ QVBoxLayout vbl;
+};
+
+template <class Type>
+class WidgetList : public GWidgetList
+{
+ public:
+ WidgetList(uint interval, QWidget *parent=0, const char *name=0)
+ : GWidgetList(interval, parent, name) {}
+
+ void append(Type *w) { GWidgetList::append(w); }
+ Type *widget(uint i) { return (Type *)GWidgetList::widget(i); }
+};
+
+#endif // PLINE_H
diff --git a/libksirtet/lib/smanager.cpp b/libksirtet/lib/smanager.cpp
new file mode 100644
index 00000000..f6588546
--- /dev/null
+++ b/libksirtet/lib/smanager.cpp
@@ -0,0 +1,115 @@
+#include "smanager.h"
+
+#include <config.h>
+
+#include <ctype.h>
+#include <netdb.h>
+#include <sys/utsname.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <strings.h>
+
+
+SocketManager::SocketManager()
+{
+ max_fd = 0;
+ FD_ZERO(&read_set);
+ FD_ZERO(&write_set);
+ nbWriteable = 0;
+}
+
+SocketManager::~SocketManager()
+{
+ clean();
+}
+
+void SocketManager::clean()
+{
+ for(uint i=0; i<sockets.size(); i++) delete sockets[i];
+ sockets.resize(0);
+}
+
+int SocketManager::find(int fd)
+{
+ for(uint i=0; i<sockets.size(); i++)
+ if ( sockets[i]->fd()==fd ) return i;
+ return -1;
+}
+
+uint SocketManager::append(Socket *socket, SocketProperty sp)
+{
+ uint s = sockets.size();
+ sockets.resize(s+1);
+ sockets[s] = socket;
+
+ max_fd = QMAX(max_fd, socket->fd());
+
+ if ( sp==ReadWrite || sp==ReadOnly ) FD_SET(socket->fd(), &read_set);
+ if ( sp==ReadWrite || sp==WriteOnly ) {
+ nbWriteable++;
+ FD_SET(socket->fd(), &write_set);
+ }
+
+ return s;
+}
+
+void SocketManager::remove(uint i, bool del)
+{
+ Socket *so = sockets[i];
+
+ uint s = sockets.size()-1;
+ for(uint j=i; j<s; j++) sockets[j] = sockets[j+1];
+ sockets.resize(s);
+
+ max_fd = 0;
+ for(uint j=0; j<s; j++)
+ max_fd = QMAX(max_fd, sockets[j]->fd());
+
+ int fd = so->fd();
+ if ( FD_ISSET(fd, &read_set) ) FD_CLR(fd, &read_set);
+ if ( FD_ISSET(fd, &write_set) ) {
+ nbWriteable--;
+ FD_CLR(fd, &write_set);
+ }
+
+ if (del) delete so;
+}
+
+bool SocketManager::canWriteAll(uint sec, uint usec)
+{
+ write_tmp = write_set;
+ tv.tv_sec = sec;
+ tv.tv_usec = usec;
+ return ( select(max_fd+1, 0, &write_tmp, 0, &tv)==(int)nbWriteable );
+}
+
+bool SocketManager::canWrite(uint i, uint sec, uint usec)
+{
+ int fd = sockets[i]->fd();
+ FD_ZERO(&write_tmp);
+ FD_SET(fd, &write_tmp);
+ tv.tv_sec = sec;
+ tv.tv_usec = usec;
+ return ( select(fd+1, 0, &write_tmp, 0, &tv)==1 );
+}
+
+bool SocketManager::checkPendingData(uint sec, uint usec)
+{
+ read_tmp = read_set;
+ tv.tv_sec = sec;
+ tv.tv_usec = usec;
+ return ( select(max_fd+1, &read_tmp, 0, 0, &tv)!=-1 );
+}
+
+bool SocketManager::dataPending(uint i)
+{
+ int fd = sockets[i]->fd();
+ return FD_ISSET(fd, &read_tmp);
+}
+
+bool SocketManager::writeCommon(uint i)
+{
+ return sockets[i]->write(writing.buffer());
+}
diff --git a/libksirtet/lib/smanager.h b/libksirtet/lib/smanager.h
new file mode 100644
index 00000000..a831b702
--- /dev/null
+++ b/libksirtet/lib/smanager.h
@@ -0,0 +1,88 @@
+#ifndef SMANAGER_H
+#define SMANAGER_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <sys/types.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h> // Needed on some systems.
+#endif
+
+#include <sys/time.h>
+
+#include "socket.h"
+
+/**
+ * The SocketManager class is useful to manage (rw, ro, wo) sockets.
+ *
+ * You must add the sockets you want to manage to this class before other
+ * operations. The sockets are stored in an array and other methods reference
+ * the sockets by their index in that array.
+ */
+class SocketManager
+{
+ public:
+ SocketManager();
+
+ /** Be aware that unremoved sockets will be closed there. */
+ ~SocketManager();
+
+ /** Remove all sockets and close them. */
+ void clean();
+
+ enum SocketProperty { ReadOnly, WriteOnly, ReadWrite };
+
+ /** @return the number of sockets. */
+ uint size() const { return sockets.size(); }
+
+ const Socket *operator[](uint i) const { return sockets[i]; }
+ Socket *operator [](uint i) { return sockets[i]; }
+
+ /** @return the index of the socket (-1 if not present). */
+ int find(int fd);
+
+ /**
+ * Append a socket at the end of the array of sockets.
+ * @param sp determines if the socket will be used for ReadWrite,
+ * ReadOnly or WriteOnly operations.
+ * @return the index of the socket.
+ */
+ uint append(Socket *, SocketProperty sp = ReadWrite);
+
+ /**
+ * Remove the socket indexed <I>i</I>. Note that the following sockets in
+ * the array will have their index decremented by one.
+ * @param deleteSocket if true, the socket is deleted
+ */
+ void remove(uint i, bool deleteSocket);
+
+ /** @return TRUE if it is possible to write to all the writeable sockets. */
+ bool canWriteAll(uint sec = 0, uint usec = 0);
+
+ /** @return TRUE if it is possible to write to the specified socket. */
+ bool canWrite(uint i, uint sec = 0, uint usec = 0);
+
+ Stream &commonWritingStream() { return writing; }
+ bool writeCommon(uint i); // do not clear stream
+
+ /**
+ * Check if there are pending data on at least one of the readeable
+ * socket.
+ */
+ bool checkPendingData(uint sec = 0, uint usec = 0);
+
+ bool dataPending(uint i);
+
+ private:
+ QMemArray<Socket *> sockets;
+
+ fd_set read_set, write_set, read_tmp, write_tmp;
+ struct timeval tv;
+ int max_fd;
+ uint nbWriteable;
+
+ WritingStream writing;
+};
+
+#endif // SMANAGER_H
diff --git a/libksirtet/lib/socket.cpp b/libksirtet/lib/socket.cpp
new file mode 100644
index 00000000..9ef3ba3c
--- /dev/null
+++ b/libksirtet/lib/socket.cpp
@@ -0,0 +1,80 @@
+#include "socket.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include <ctype.h>
+#include <netdb.h>
+#include <sys/utsname.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h> // for FIONREAD
+#endif
+
+Socket::Socket(KExtendedSocket *s, bool createNotifier,
+ QObject *parent, const char *name)
+: _socket(s), _notifier(0)
+{
+ Q_ASSERT(s);
+ if (createNotifier) {
+ _notifier = new QSocketNotifier(s->fd(), QSocketNotifier::Read,
+ parent, name);
+ _notifier->setEnabled(FALSE);
+ }
+}
+
+Socket::~Socket()
+{
+ delete _notifier;
+ delete _socket;
+}
+
+bool Socket::write(const QByteArray &a)
+{
+ return ( _socket->writeBlock(a.data(), a.size())==(int)a.size() );
+}
+
+bool Socket::write()
+{
+ bool res = write(writing.buffer());
+ writing.clear();
+ return res;
+}
+
+int Socket::pendingData() const
+{
+ int size = 0;
+ if ( ioctl(_socket->fd(), FIONREAD, (char *)&size)<0 ) return -1;
+ return size;
+}
+
+int Socket::read()
+{
+ reading.clearRead();
+
+ int size = pendingData();
+ if ( size==-1 ) return -1;
+
+ reading.device()->close();
+ int dec = reading.size();
+ reading.buffer().resize(dec + size);
+ size = _socket->readBlock(reading.buffer().data() + dec, size);
+ if ( size==-1 ) reading.buffer().resize(dec);
+ reading.device()->open(IO_ReadOnly);
+
+ return size;
+}
+
+int Socket::accept(KExtendedSocket *&s)
+{
+ return _socket->accept(s);
+}
diff --git a/libksirtet/lib/socket.h b/libksirtet/lib/socket.h
new file mode 100644
index 00000000..a2f47a63
--- /dev/null
+++ b/libksirtet/lib/socket.h
@@ -0,0 +1,65 @@
+#ifndef SOCKET_H
+#define SOCKET_H
+
+#include <qsocketnotifier.h>
+
+#include <kextsock.h>
+
+#include "types.h"
+
+
+class Socket
+{
+ public:
+ Socket(KExtendedSocket *, bool createNotifier = FALSE,
+ QObject *parent = 0, const char *name = 0);
+
+ /** close the socket */
+ ~Socket();
+
+ int fd() const { return _socket->fd(); }
+
+ /**
+ * Accept a new socket.
+ */
+ int accept(KExtendedSocket *&);
+
+ /**
+ * @return the socket notifier associated with the socket
+ * (0 if none).
+ */
+ QSocketNotifier *notifier() const { return _notifier; }
+
+ /**
+ * Write data contained in the writing stream to the socket.
+ * It clears the stream.
+ * @return TRUE if all was written without error.
+ */
+ bool write();
+ bool write(const QByteArray &a);
+
+ /** @return the QDataStream for writing. */
+ WritingStream &writingStream() { return writing; }
+
+ /** @return the size of pending data. */
+ int pendingData() const;
+
+ /**
+ * Read data from socket and append them to reading stream for the specified socket.
+ * The portion of the stream that has been read is cleared.
+ * @return the read size or -1 on error
+ */
+ int read();
+
+ /** @return the reading stream. */
+ ReadingStream &readingStream() { return reading; }
+
+ private:
+ KExtendedSocket *_socket;
+ QSocketNotifier *_notifier;
+
+ WritingStream writing;
+ ReadingStream reading;
+};
+
+#endif // SOCKET_H
diff --git a/libksirtet/lib/types.cpp b/libksirtet/lib/types.cpp
new file mode 100644
index 00000000..557fffff
--- /dev/null
+++ b/libksirtet/lib/types.cpp
@@ -0,0 +1,254 @@
+#include "types.h"
+
+#include <klocale.h>
+#include "version.h"
+
+cId::cId(const QString &_gameName, const QString &_gameId)
+: libId(MULTI_ID), gameName(_gameName), gameId(_gameId)
+{}
+
+void cId::check(const cId &id)
+{
+ if ( libId!=id.libId ) state = LibIdClash;
+ else if ( gameName!=id.gameName ) state = GameNameClash;
+ else if ( gameId!=id.gameId ) state = GameIdClash;
+ else state = Accepted;
+}
+
+QString cId::errorMessage(const cId &id) const
+{
+ QString str = i18n("\nServer: \"%1\"\nClient: \"%2\"");
+
+ switch (state) {
+ case Accepted: return QString::null;
+ case LibIdClash:
+ return i18n("The MultiPlayer library of the server is incompatible")
+ + str.arg(libId).arg(id.libId);
+ case GameNameClash:
+ return i18n("Trying to connect a server for another game type")
+ + str.arg(gameName).arg(id.gameName);
+ case GameIdClash:
+ return i18n("The server game version is incompatible")
+ + str.arg(gameId).arg(id.gameId);
+ }
+ Q_ASSERT(0);
+ return QString::null;
+}
+
+QDataStream &operator << (QDataStream &s, const cId &id)
+{
+ s << id.libId << id.gameName << id.gameId << (Q_UINT8)id.state;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, cId &id)
+{
+ Q_UINT8 state;
+ s >> id.libId >> id.gameName >> id.gameId >> state;
+ id.state = (cId::State)state;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+QDataStream &operator << (QDataStream &s, const MeetingMsgFlag &f)
+{
+ s << (Q_UINT8)f;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, MeetingMsgFlag &f)
+{
+ Q_UINT8 i;
+ s >> i; f = (MeetingMsgFlag)i;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+QDataStream &operator << (QDataStream &s, const TextInfo &ti)
+{
+ s << (Q_UINT32)ti.i << ti.text;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, TextInfo &ti)
+{
+ Q_UINT32 i;
+ s >> i >> ti.text; ti.i = i;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+QDataStream &operator << (QDataStream &s, const MeetingCheckBox::Type &t)
+{
+ s << (Q_UINT8)t;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, MeetingCheckBox::Type &t)
+{
+ Q_UINT8 i;
+ s >> i; t = (MeetingCheckBox::Type)i;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+QDataStream &operator << (QDataStream &s, const TypeInfo &ti)
+{
+ s << (Q_UINT32)ti.i << ti.type;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, TypeInfo &ti)
+{
+ Q_UINT32 i;
+ s >> i >> ti.type; ti.i = i;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+QDataStream &operator << (QDataStream &s, const BoardData &bd)
+{
+ s << (Q_UINT8)bd.type << bd.name;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, BoardData &bd)
+{
+ Q_UINT8 i;
+ s >> i >> bd.name;
+ bd.type = (PlayerComboBox::Type)i;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+QDataStream &operator << (QDataStream &s, const ExtData &ed)
+{
+ s << ed.bds << ed.text << ed.type;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, ExtData &ed)
+{
+ s >> ed.bds >> ed.text >> ed.type;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+QDataStream &operator << (QDataStream &s, const MeetingLineData &pld)
+{
+ s << pld.ed << (Q_UINT8)pld.own;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, MeetingLineData &pld)
+{
+ Q_UINT8 b;
+ s >> pld.ed >> b; pld.own = b;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+QDataStream &operator << (QDataStream &s, const MetaFlag &f)
+{
+ s << (Q_UINT8)f;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, MetaFlag &f)
+{
+ Q_UINT8 i;
+ s >> i; f = (MetaFlag)i;
+ return s;
+}
+
+
+//-----------------------------------------------------------------------------
+Stream::Stream(int _mode)
+: mode(_mode)
+{
+ setDevice(&buf);
+ Q_ASSERT( _mode==IO_ReadOnly || _mode==IO_WriteOnly );
+ buf.open(_mode);
+}
+
+void Stream::clear()
+{
+ buf.close();
+ buf.open(mode | IO_Truncate);
+}
+
+void Stream::setArray(QByteArray a)
+{
+ buf.close();
+ buf.setBuffer(a);
+ buf.open(mode);
+}
+
+bool ReadingStream::readOk()
+{
+ return ( buf.status()==IO_Ok );
+}
+
+void ReadingStream::clearRead()
+{
+ int i = buf.at();
+ if ( i==0 ) return;
+ buf.close();
+ QByteArray a;
+ a.duplicate(buffer().data() + i, size() - i);
+ buf.setBuffer(a);
+ buf.open(IO_ReadOnly);
+}
+
+//-----------------------------------------------------------------------------
+void IOBuffer::writingToReading()
+{
+ // this should do the trick :)
+ reading.setArray(writing.buffer());
+ QByteArray a;
+ writing.setArray(a);
+}
+
+//-----------------------------------------------------------------------------
+void BufferArray::clear(uint k)
+{
+ for (uint i=k; i<a.size(); i++) delete a[i];
+}
+
+BufferArray::~BufferArray()
+{
+ clear(0);
+}
+
+void BufferArray::resize(uint nb)
+{
+ uint s = a.size();
+ if ( nb<s ) clear(nb);
+ a.resize(nb);
+ for (uint i=s; i<nb; i++) a[i] = new IOBuffer;
+}
+
+QDataStream &operator <<(QDataStream &s, const BufferArray &b)
+{
+ for (uint i=0; i<b.size(); i++) {
+ s.writeBytes(b[i]->writing.buffer().data(), b[i]->writing.size());
+// debug("BUFFERARRAY : << (i=%i size=%i)", i, b[i]->writing.size());
+ b[i]->writing.clear();
+ }
+ return s;
+}
+
+QDataStream &operator >>(QDataStream &s, BufferArray &b)
+{
+ uint size;
+ char *c;
+ for (uint i=0; i<b.size(); i++) {
+ s.readBytes(c, size);
+ QByteArray a;
+ a.assign(c, size);
+ b[i]->reading.setArray(a);
+// debug("BUFFERARRAY : >> (i=%i c=%i size=%i s=%i)",
+// i, (int)c, size, b[i]->reading.size());
+ }
+ return s;
+}
diff --git a/libksirtet/lib/types.h b/libksirtet/lib/types.h
new file mode 100644
index 00000000..2ccdcba0
--- /dev/null
+++ b/libksirtet/lib/types.h
@@ -0,0 +1,197 @@
+#ifndef MTYPES_H
+#define MTYPES_H
+
+#include <qstring.h>
+#include <qbuffer.h>
+#include <qvaluelist.h>
+
+#include "miscui.h"
+
+
+/** Internal class : used for client identification. */
+class cId
+{
+ public:
+ cId() {}
+ cId(const QString &gameName, const QString &gameId);
+
+ enum State { Accepted, LibIdClash, GameNameClash, GameIdClash };
+ void check(const cId &id);
+ bool accepted() const { return state==Accepted; }
+ QString errorMessage(const cId &id) const;
+
+ friend QDataStream &operator << (QDataStream &s, const cId &id);
+ friend QDataStream &operator >> (QDataStream &s, cId &id);
+
+ private:
+ QString libId, gameName, gameId;
+ State state;
+};
+QDataStream &operator << (QDataStream &s, const cId &id);
+QDataStream &operator >> (QDataStream &s, cId &id);
+
+/** Flags used for the netmeeting. */
+enum MeetingMsgFlag
+ { IdFlag = 0, EndFlag, NewFlag, DelFlag, Mod_TextFlag, Mod_TypeFlag, Mod_OptFlag, PlayFlag };
+QDataStream &operator << (QDataStream &s, const MeetingMsgFlag &f);
+QDataStream &operator >> (QDataStream &s, MeetingMsgFlag &f);
+
+/** Internal class : used in netmeeting to transport text line. */
+class TextInfo
+{
+ public:
+ TextInfo() {}
+
+ uint i;
+ QString text;
+};
+QDataStream &operator << (QDataStream &s, const TextInfo &ti);
+QDataStream &operator >> (QDataStream &s, TextInfo &ti);
+
+/** Internal class : used in netmeeting to transport readiness status. */
+typedef struct {
+ uint i;
+ MeetingCheckBox::Type type;
+} TypeInfo;
+QDataStream &operator << (QDataStream &s, const MeetingCheckBox::Type &t);
+QDataStream &operator >> (QDataStream &s, MeetingCheckBox::Type &t);
+QDataStream &operator << (QDataStream &s, const TypeInfo &ti);
+QDataStream &operator >> (QDataStream &s, TypeInfo &ti);
+
+/* Internal class : store game data. */
+class BoardData
+{
+ public:
+ BoardData() {}
+
+ QString name;
+ PlayerComboBox::Type type;
+};
+QDataStream &operator <<(QDataStream &, const BoardData &);
+QDataStream &operator >>(QDataStream &, BoardData &);
+
+/* Internal class : store extended game data (used in netmeeting). */
+class ExtData
+{
+ public:
+ ExtData() {}
+ ExtData(const QValueList<BoardData> &_bds, const QString &_text,
+ MeetingCheckBox::Type _type)
+ : bds(_bds), text(_text), type(_type) {}
+
+ QValueList<BoardData> bds;
+ QString text;
+ MeetingCheckBox::Type type;
+};
+QDataStream &operator << (QDataStream &s, const ExtData &ed);
+QDataStream &operator >> (QDataStream &s, ExtData &ed);
+
+/* Internal class : store meeting line data (in netmeeting). */
+class MeetingLineData
+{
+ public:
+ MeetingLineData() {}
+
+ ExtData ed;
+ bool own;
+};
+QDataStream &operator << (QDataStream &s, const MeetingLineData &pld);
+QDataStream &operator >> (QDataStream &s, MeetingLineData &pld);
+
+/* Internal class : store remote host data. */
+class Socket;
+
+class RemoteHostData
+{
+ public:
+ RemoteHostData() : socket(0) {}
+
+ Socket *socket;
+ QValueList<BoardData> bds;
+};
+
+/* Internal class : store connection data (used by config. wizard). */
+class ConnectionData
+{
+ public:
+ ConnectionData() {}
+
+ bool network, server;
+ RemoteHostData rhd;
+};
+
+/** Flags used for network communication. */
+enum MetaFlag { MF_Ask = 0, MF_Data };
+QDataStream &operator << (QDataStream &s, const MetaFlag &f);
+QDataStream &operator >> (QDataStream &s, MetaFlag &f);
+
+/** Internal class : encapsulate read/write QBuffer. */
+class Stream : public QDataStream
+{
+ public:
+ Stream(int mode);
+
+ void clear();
+ void setArray(QByteArray a);
+
+ QByteArray buffer() const { return buf.buffer(); }
+ uint size() const { return buf.buffer().size(); }
+
+ protected:
+ QBuffer buf;
+
+ private:
+ int mode;
+};
+
+/** Internal class : encapsulate write QBuffer. */
+class WritingStream : public Stream
+{
+ public:
+ WritingStream() : Stream(IO_WriteOnly) {}
+};
+
+/** Internal class : encapsulate read QBuffer. */
+class ReadingStream : public Stream
+{
+ public:
+ ReadingStream() : Stream(IO_ReadOnly) {}
+
+ bool readOk();
+ void clearRead();
+};
+
+/** Internal class : include a @ref ReadingStream and a @ref WritingStream. */
+class IOBuffer
+{
+ public:
+ IOBuffer() {}
+
+ void writingToReading();
+
+ ReadingStream reading;
+ WritingStream writing;
+};
+
+/** Internal class : array of @ref IOBuffer. */
+class BufferArray
+{
+ public:
+ BufferArray() {}
+ BufferArray(uint nb) { resize(nb); }
+ ~BufferArray();
+
+ void resize(uint nb);
+
+ uint size() const { return a.size(); }
+ IOBuffer *operator [](uint i) const { return a[i]; }
+
+ private:
+ QMemArray<IOBuffer *> a;
+
+ void clear(uint nb);
+};
+QDataStream &operator <<(QDataStream &s, const BufferArray &b);
+QDataStream &operator >>(QDataStream &s, BufferArray &b);
+
+#endif // MTYPES_H
diff --git a/libksirtet/lib/version.h b/libksirtet/lib/version.h
new file mode 100644
index 00000000..49dc7b6b
--- /dev/null
+++ b/libksirtet/lib/version.h
@@ -0,0 +1,6 @@
+#define MULTI_VERSION "0.1.8"
+#define MULTI_LONG_VERSION "0.1.8 (11 April 2001)"
+#define MULTI_COPYLEFT "(c) 1998-2001, Nicolas Hadacek"
+
+#define MULTI_ID "003" // should be increased when incompatible
+ // changes are made.
diff --git a/libksirtet/lib/wizard.cpp b/libksirtet/lib/wizard.cpp
new file mode 100644
index 00000000..30d5a89d
--- /dev/null
+++ b/libksirtet/lib/wizard.cpp
@@ -0,0 +1,229 @@
+#include "wizard.h"
+#include "wizard.moc"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <qvbuttongroup.h>
+#include <qradiobutton.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qsignalmapper.h>
+#include <qvgroupbox.h>
+#include <qgrid.h>
+#include <qfile.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kdialogbase.h>
+
+#include "types.h"
+#include "defines.h"
+#include "socket.h"
+
+#ifdef __bsdi__
+#define IPPORT_USERRESERVED IPPORT_DYNAMIC
+#endif
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__sgi)
+#define IPPORT_USERRESERVED IPPORT_RESERVED
+#endif
+#define MIN_USER_PORT (unsigned short int)IPPORT_USERRESERVED
+#define MAX_USER_PORT 65535
+
+MPWizard::MPWizard(const MPGameInfo &gi, ConnectionData &_cd,
+ QWidget *parent, const char *name)
+: KWizard(parent, name, TRUE), cd(_cd)
+{
+// setupTypePage(); // #### REMOVE NETWORK GAMES UNTIL FIXED
+ type = Local;
+ setupLocalPage(gi);
+}
+
+//-----------------------------------------------------------------------------
+void MPWizard::setupTypePage()
+{
+ KConfigGroupSaver cg(kapp->config(), MP_GROUP);
+
+ typePage = new QVBox(this);
+ typePage->setMargin(KDialogBase::marginHint());
+
+ QVButtonGroup *vbg = new QVButtonGroup(typePage);
+ connect(vbg, SIGNAL(clicked(int)), SLOT(typeChanged(int)));
+ QRadioButton *b;
+ b = new QRadioButton(i18n("Create a local game"), vbg);
+ b = new QRadioButton(i18n("Create a network game"), vbg);
+ b = new QRadioButton(i18n("Join a network game"), vbg);
+ type = (Type)cg.config()->readNumEntry(MP_GAMETYPE, 0);
+ if ( type<0 || type>2 ) type = Local;
+ vbg->setButton(type);
+
+ typePage->setSpacing(KDialogBase::spacingHint());
+ net = new QVGroupBox(i18n("Network Settings"), typePage);
+ QGrid *grid = new QGrid(2, net);
+ lserver = new QLabel(" ", grid);
+ grid->setSpacing(KDialogBase::spacingHint());
+ eserver = new QLineEdit(grid);
+ (void)new QLabel(i18n("Port:"), grid);
+ int p = cg.config()->readNumEntry(MP_PORT, (uint)MIN_USER_PORT);
+ eport = new KIntNumInput(p, grid);
+ eport->setRange(MIN_USER_PORT, MAX_USER_PORT, 1, false);
+
+ addPage(typePage, i18n("Choose Game Type"));
+ setHelpEnabled(typePage, FALSE);
+ typeChanged(type);
+}
+
+//-----------------------------------------------------------------------------
+void MPWizard::setupLocalPage(const MPGameInfo &gi)
+{
+ localPage = new QVBox(this);
+ localPage->setMargin(KDialogBase::marginHint());
+
+ wl = new WidgetList<PlayerLine>(5, localPage);
+ QSignalMapper *husm = new QSignalMapper(this);
+ if (gi.humanSettingSlot) connect(husm, SIGNAL(mapped(int)),
+ gi.humanSettingSlot);
+ QSignalMapper *aism = new QSignalMapper(this);
+ if (gi.AISettingSlot) connect(aism, SIGNAL(mapped(int)), gi.AISettingSlot);
+
+ KConfigGroupSaver cg(kapp->config(), MP_GROUP);
+ QString n;
+ PlayerComboBox::Type type;
+ PlayerLine *pl;
+ Q_ASSERT( gi.maxNbLocalPlayers>0 );
+ for (uint i=0; i<gi.maxNbLocalPlayers; i++) {
+ type = (PlayerComboBox::Type)
+ cg.config()->readNumEntry(QString(MP_PLAYER_TYPE).arg(i),
+ (i==0 ? PlayerComboBox::Human : PlayerComboBox::None));
+ n = cg.config()->readEntry(QString(MP_PLAYER_NAME).arg(i),
+ i18n("Player #%1").arg(i));
+
+ pl = new PlayerLine(type, n, gi.humanSettingSlot, gi.AISettingSlot,
+ i!=0, gi.AIAllowed, wl);
+ connect(pl, SIGNAL(typeChanged(int)), SLOT(lineTypeChanged(int)));
+ husm->setMapping(pl, i);
+ connect(pl, SIGNAL(setHuman()), husm, SLOT(map()));
+ aism->setMapping(pl, i);
+ connect(pl, SIGNAL(setAI()), aism, SLOT(map()));
+ wl->append(pl);
+ }
+
+ ((QVBox *)localPage)->setSpacing(KDialogBase::spacingHint());
+
+// keys = new QPushButton(i18n("Configure Keys..."), localPage);
+// connect(keys, SIGNAL(clicked()), SLOT(configureKeysSlot()));
+
+ addPage(localPage, i18n("Local Player's Settings"));
+ setHelpEnabled(localPage, FALSE);
+ lineTypeChanged(0);
+}
+
+QString MPWizard::name(uint i) const
+{
+ QString s = wl->widget(i)->name();
+ if ( s.length()==0 ) s = i18n("Player #%1").arg(i);
+ return s;
+}
+
+void MPWizard::typeChanged(int t)
+{
+ type = (Type)t;
+
+ QString str;
+ if ( type!=Client ) {
+ str = "localhost";
+ lserver->setText(i18n("Hostname:"));
+ } else {
+ KConfigGroupSaver cg(kapp->config(), MP_GROUP);
+ str = cg.config()->readEntry(MP_SERVER_ADDRESS,
+ i18n("the.server.address"));
+ lserver->setText(i18n("Server address:"));
+ }
+ eserver->setText(str);
+ eserver->setEnabled(type==Client);
+ eport->setEnabled(type!=Local);
+ net->setEnabled(type!=Local);
+}
+
+void MPWizard::lineTypeChanged(int)
+{
+ bool b = FALSE;
+ for (uint i=0; i<wl->size(); i++)
+ if ( wl->widget(i)->type()==PlayerComboBox::Human ) {
+ b = TRUE;
+ break;
+ }
+// keys->setEnabled(b);
+}
+
+void MPWizard::accept()
+{
+ KConfigGroupSaver cg(kapp->config(), MP_GROUP);
+
+ cd.network = ( type!=Local );
+ cd.server = ( type!=Client );
+
+ if (cd.network) {
+ //**********************************************************
+ // create socket
+ int flags = KExtendedSocket::inetSocket
+ | KExtendedSocket::streamSocket;
+ if (cd.server) flags |= KExtendedSocket::passiveSocket;
+ QString host = QFile::encodeName(eserver->text());
+ KExtendedSocket *socket
+ = new KExtendedSocket(host, eport->value(), flags);
+
+ // do lookup
+ int res = socket->lookup();
+ if ( checkSocket(res, socket, i18n("Error looking up for \"%1\"")
+ .arg(host), this) ) {
+ delete socket;
+ return;
+ }
+
+ // connect (client) or listen (server)
+ res = (cd.server ? socket->listen() : socket->connect());
+ if ( checkSocket(res, socket, i18n("Error opening socket"), this) ) {
+ delete socket;
+ return;
+ }
+
+ cd.rhd.socket = new Socket(socket, true);
+
+ if ( !cd.server )
+ cg.config()->writeEntry(MP_SERVER_ADDRESS, eserver->text());
+ cg.config()->writeEntry(MP_PORT, eport->value());
+ }
+
+ BoardData bd;
+ for (uint i=0; i<wl->size(); i++) {
+ if ( wl->widget(i)->type()==PlayerComboBox::None ) continue;
+ bd.name = name(i);
+ bd.type = wl->widget(i)->type();
+ cd.rhd.bds += bd;
+ }
+
+ cg.config()->writeEntry(MP_GAMETYPE, (int)type);
+ for (uint i=0; i<wl->size(); i++) {
+ cg.config()->writeEntry(QString(MP_PLAYER_TYPE).arg(i),
+ (int)wl->widget(i)->type());
+ cg.config()->writeEntry(QString(MP_PLAYER_NAME).arg(i), name(i));
+ }
+
+ KWizard::accept();
+}
+
+void MPWizard::showPage(QWidget *page)
+{
+ if ( page==localPage ) setFinishEnabled(localPage, TRUE);
+ KWizard::showPage(page);
+}
+
+void MPWizard::configureKeysSlot()
+{
+ uint nb = 0;
+ for (uint i=0; i<wl->size(); i++)
+ if ( wl->widget(i)->type()==PlayerComboBox::Human ) nb++;
+ emit configureKeys(nb);
+}
diff --git a/libksirtet/lib/wizard.h b/libksirtet/lib/wizard.h
new file mode 100644
index 00000000..29287508
--- /dev/null
+++ b/libksirtet/lib/wizard.h
@@ -0,0 +1,57 @@
+#ifndef WIZARD_H
+#define WIZARD_H
+
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qvbox.h>
+#include <qvgroupbox.h>
+#include <qpushbutton.h>
+
+#include <knuminput.h>
+#include <kconfig.h>
+#include <kwizard.h>
+
+#include "pline.h"
+#include "mp_interface.h"
+
+class ConnectionData;
+
+class MPWizard : public KWizard
+{
+ Q_OBJECT
+
+ public:
+ MPWizard(const MPGameInfo &gi, ConnectionData &cd,
+ QWidget *parent = 0, const char *name = 0);
+
+ void showPage(QWidget *page);
+
+ signals:
+ void configureKeys(uint);
+
+ protected slots:
+ void accept();
+
+ private slots:
+ void typeChanged(int t);
+ void lineTypeChanged(int);
+ void configureKeysSlot();
+
+ private:
+ ConnectionData &cd;
+ enum Type { Local, Server, Client };
+ Type type;
+ QVBox *typePage, *localPage;
+ WidgetList<PlayerLine> *wl;
+ QLabel *lserver;
+ QLineEdit *eserver;
+ KIntNumInput *eport;
+ QVGroupBox *net;
+// QPushButton *keys;
+
+ void setupTypePage();
+ void setupLocalPage(const MPGameInfo &gi);
+ QString name(uint i) const;
+};
+
+#endif // WIZARD_H