eliot-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Eliot-dev] eliot game/coord.cpp game/coord.h game/Makefile...


From: Olivier Teulière
Subject: [Eliot-dev] eliot game/coord.cpp game/coord.h game/Makefile...
Date: Thu, 19 Feb 2009 18:25:21 +0000

CVSROOT:        /cvsroot/eliot
Module name:    eliot
Changes by:     Olivier Teulière <ipkiss>       09/02/19 18:25:21

Modified files:
        game           : coord.cpp coord.h Makefile.am training.cpp 
        qt             : Makefile.am board_widget.cpp board_widget.h 
                         main_window.cpp main_window.h player_widget.cpp 
                         player_widget.h training_widget.cpp 
                         training_widget.h 
        qt/ui          : player_widget.ui training_widget.ui 
Added files:
        qt             : coord_model.cpp coord_model.h 
                         play_word_mediator.cpp play_word_mediator.h 

Log message:
         - Allow setting the coordinates of the played word by clicking on the 
board
         - New PlayWordMediator class, to encapsulate the behaviour of the 
controls
           used to play a word
         - The Training mode now has input fields to play words directly, 
without
           choosing them in the search results

CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/eliot/game/coord.cpp?cvsroot=eliot&r1=1.11&r2=1.12
http://cvs.savannah.gnu.org/viewcvs/eliot/game/coord.h?cvsroot=eliot&r1=1.9&r2=1.10
http://cvs.savannah.gnu.org/viewcvs/eliot/game/Makefile.am?cvsroot=eliot&r1=1.24&r2=1.25
http://cvs.savannah.gnu.org/viewcvs/eliot/game/training.cpp?cvsroot=eliot&r1=1.33&r2=1.34
http://cvs.savannah.gnu.org/viewcvs/eliot/qt/Makefile.am?cvsroot=eliot&r1=1.9&r2=1.10
http://cvs.savannah.gnu.org/viewcvs/eliot/qt/board_widget.cpp?cvsroot=eliot&r1=1.11&r2=1.12
http://cvs.savannah.gnu.org/viewcvs/eliot/qt/board_widget.h?cvsroot=eliot&r1=1.5&r2=1.6
http://cvs.savannah.gnu.org/viewcvs/eliot/qt/main_window.cpp?cvsroot=eliot&r1=1.29&r2=1.30
http://cvs.savannah.gnu.org/viewcvs/eliot/qt/main_window.h?cvsroot=eliot&r1=1.15&r2=1.16
http://cvs.savannah.gnu.org/viewcvs/eliot/qt/player_widget.cpp?cvsroot=eliot&r1=1.14&r2=1.15
http://cvs.savannah.gnu.org/viewcvs/eliot/qt/player_widget.h?cvsroot=eliot&r1=1.9&r2=1.10
http://cvs.savannah.gnu.org/viewcvs/eliot/qt/training_widget.cpp?cvsroot=eliot&r1=1.9&r2=1.10
http://cvs.savannah.gnu.org/viewcvs/eliot/qt/training_widget.h?cvsroot=eliot&r1=1.5&r2=1.6
http://cvs.savannah.gnu.org/viewcvs/eliot/qt/coord_model.cpp?cvsroot=eliot&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/eliot/qt/coord_model.h?cvsroot=eliot&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/eliot/qt/play_word_mediator.cpp?cvsroot=eliot&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/eliot/qt/play_word_mediator.h?cvsroot=eliot&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/eliot/qt/ui/player_widget.ui?cvsroot=eliot&r1=1.4&r2=1.5
http://cvs.savannah.gnu.org/viewcvs/eliot/qt/ui/training_widget.ui?cvsroot=eliot&r1=1.3&r2=1.4

Patches:
Index: game/coord.cpp
===================================================================
RCS file: /cvsroot/eliot/eliot/game/coord.cpp,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -b -r1.11 -r1.12
--- game/coord.cpp      22 Nov 2008 13:09:30 -0000      1.11
+++ game/coord.cpp      19 Feb 2009 18:25:17 -0000      1.12
@@ -45,12 +45,23 @@
             m_col >= BOARD_MIN && m_col <= BOARD_MAX);
 }
 
+/*
 void Coord::operator=(const Coord &iOther)
 {
     m_dir = iOther.m_dir;
     m_row = iOther.m_row;
     m_col = iOther.m_col;
 }
+*/
+
+bool Coord::operator==(const Coord &iOther) const
+{
+    if (!isValid() && !iOther.isValid())
+        return true;
+    return m_row == iOther.m_row
+        && m_col == iOther.m_col
+        && m_dir == iOther.m_dir;
+}
 
 void Coord::swap()
 {

Index: game/coord.h
===================================================================
RCS file: /cvsroot/eliot/eliot/game/coord.h,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -b -r1.9 -r1.10
--- game/coord.h        22 Nov 2008 13:09:30 -0000      1.9
+++ game/coord.h        19 Feb 2009 18:25:17 -0000      1.10
@@ -50,7 +50,7 @@
     Direction getDir() const    { return m_dir; }
 
     bool isValid() const;
-    void operator=(const Coord &iOther);
+    bool operator==(const Coord &iOther) const;
 
     // Swap the coordinates (without changing the direction)
     void swap();

Index: game/Makefile.am
===================================================================
RCS file: /cvsroot/eliot/eliot/game/Makefile.am,v
retrieving revision 1.24
retrieving revision 1.25
diff -u -b -r1.24 -r1.25
--- game/Makefile.am    24 Jan 2009 23:02:42 -0000      1.24
+++ game/Makefile.am    19 Feb 2009 18:25:17 -0000      1.25
@@ -24,6 +24,8 @@
 libgame_a_SOURCES= \
     game_exception.cpp game_exception.h \
     command.h command.cpp \
+    coord.cpp coord.h \
+    cross.cpp cross.h \
     rack.cpp rack.h \
     pldrack.cpp pldrack.h \
     round.cpp round.h \
@@ -38,8 +40,6 @@
     player_rack_cmd.cpp player_rack_cmd.h \
     ai_player.h \
     ai_percent.cpp ai_percent.h \
-    coord.cpp coord.h \
-    cross.cpp cross.h \
     board.cpp board.h \
     board_cross.cpp \
     matrix.h \

Index: game/training.cpp
===================================================================
RCS file: /cvsroot/eliot/eliot/game/training.cpp,v
retrieving revision 1.33
retrieving revision 1.34
diff -u -b -r1.33 -r1.34
--- game/training.cpp   24 Jan 2009 17:44:57 -0000      1.33
+++ game/training.cpp   19 Feb 2009 18:25:17 -0000      1.34
@@ -88,6 +88,8 @@
     // Perform all the validity checks, and fill a round
     Round round;
 
+    removeTestPlay();
+
     int res = checkPlayedWord(iCoord, iWord, round);
     if (res != 0)
     {

Index: qt/Makefile.am
===================================================================
RCS file: /cvsroot/eliot/eliot/qt/Makefile.am,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -b -r1.9 -r1.10
--- qt/Makefile.am      30 Nov 2008 20:53:45 -0000      1.9
+++ qt/Makefile.am      19 Feb 2009 18:25:17 -0000      1.10
@@ -47,12 +47,14 @@
     ui/training_widget.ui.h \
     ui/prefs_dialog.ui.h \
     ui/dic_tools_widget.ui.h \
+    coord_model.moc.cpp \
     new_game.moc.cpp \
     dic_tools_widget.moc.cpp \
     bag_widget.moc.cpp \
     score_widget.moc.cpp \
     board_widget.moc.cpp \
     history_widget.moc.cpp \
+    play_word_mediator.moc.cpp \
     player_widget.moc.cpp \
     training_widget.moc.cpp \
     prefs_dialog.moc.cpp \
@@ -62,12 +64,14 @@
 
 eliot_SOURCES = \
     qtcommon.h \
+    coord_model.h coord_model.cpp \
     bag_widget.cpp bag_widget.h \
     dic_tools_widget.cpp dic_tools_widget.h \
     new_game.cpp new_game.h \
     score_widget.cpp score_widget.h \
     board_widget.cpp board_widget.h \
     history_widget.cpp history_widget.h \
+    play_word_mediator.cpp play_word_mediator.h \
     training_widget.cpp training_widget.h \
     player_widget.cpp player_widget.h \
     prefs_dialog.cpp prefs_dialog.h \

Index: qt/board_widget.cpp
===================================================================
RCS file: /cvsroot/eliot/eliot/qt/board_widget.cpp,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -b -r1.11 -r1.12
--- qt/board_widget.cpp 26 Jan 2009 20:30:41 -0000      1.11
+++ qt/board_widget.cpp 19 Feb 2009 18:25:17 -0000      1.12
@@ -21,6 +21,7 @@
 #include <math.h>
 #include <QtGui/QPainter>
 #include <QtGui/QPaintEvent>
+#include <QtGui/QMouseEvent>
 #include <QtCore/QSettings>
 
 #include "board_widget.h"
@@ -29,6 +30,7 @@
 #include "public_game.h"
 #include "tile.h"
 #include "board.h"
+#include "coord_model.h"
 
 using namespace std;
 
@@ -42,16 +44,21 @@
 const QColor BoardWidget::PreviewColour(183, 183, 123);
 const QColor BoardWidget::NormalColour(0, 0, 0);
 const QColor BoardWidget::JokerColour(255, 0, 0);
+const QColor BoardWidget::ArrowColour(10, 10, 10);
 
 
-BoardWidget::BoardWidget(QWidget *parent)
-    : QFrame(parent), m_game(NULL)
+BoardWidget::BoardWidget(CoordModel &iCoordModel, QWidget *parent)
+    : QFrame(parent), m_game(NULL), m_coordModel(iCoordModel)
 {
     setFrameStyle(QFrame::Panel);
     // Use as much space as possible
     QSizePolicy policy(QSizePolicy::Expanding, QSizePolicy::Expanding);
     setSizePolicy(policy);
     setMinimumSize(200, 200);
+
+    // Listen to changes in the coordinates
+    QObject::connect(&m_coordModel, SIGNAL(coordChanged(const Coord&)),
+                     this, SLOT(updateArrow(const Coord&)));
 }
 
 
@@ -62,6 +69,14 @@
 }
 
 
+void BoardWidget::updateArrow(const Coord &)
+{
+    // Refresh everything
+    // We could actually refresh only the 2 involved squares...
+    refresh();
+}
+
+
 void BoardWidget::refresh()
 {
     update();
@@ -158,5 +173,64 @@
                          Qt::AlignCenter,
                          QString(1, 'A' + x - 1));
     }
+    // Draw the arrow
+    const Coord &markCoord = m_coordModel.getCoord();
+    if (m_game != NULL && markCoord.isValid())
+    {
+        const unsigned int xPos = (markCoord.getCol() - BOARD_MIN + 1) * 
squareSize + 1;
+        const unsigned int yPos = (markCoord.getRow() - BOARD_MIN + 1) * 
squareSize + 1;
+        painter.setPen(QPen(ArrowColour, 0));
+        painter.setBrush(ArrowColour);
+        const int mid = squareSize / 2;
+        const int fifth = squareSize / 5;
+        const int width = squareSize / 16;
+        painter.translate(xPos + mid, yPos + mid);
+        if (markCoord.getDir() == Coord::VERTICAL)
+            painter.rotate(90);
+        const QPoint points[] =
+        {
+            QPoint(-mid + fifth, -width),
+            QPoint(-mid + 3*fifth, -width),
+            QPoint(-mid + 3*fifth, -fifth),
+            QPoint(-mid + 4*fifth, 0),
+            QPoint(-mid + 3*fifth, fifth),
+            QPoint(-mid + 3*fifth, width),
+            QPoint(-mid + fifth, width)
+        };
+        painter.drawPolygon(points, 7);
+
+        painter.setPen(QPen());
+        painter.setBrush(NormalColour);
+    }
+}
+
+
+void BoardWidget::mousePressEvent(QMouseEvent *iEvent)
+{
+    if (m_game == NULL)
+    {
+        m_coordModel.clear();
+        return;
+    }
+
+    if (iEvent->button() == Qt::LeftButton)
+    {
+        // Find the coordinates
+        const int size = std::min(width(), height());
+        const int squareSize = (int)floor((size - 1) / (BOARD_MAX - BOARD_MIN 
+ 2));
+        int row = iEvent->y() / squareSize;
+        int col = iEvent->x() / squareSize;
+        // Change the direction if this is exactly the same as the current one
+        Coord coord(row, col, Coord::HORIZONTAL);
+        if (m_coordModel.getCoord() == coord)
+            coord.setDir(Coord::VERTICAL);
+        // Take into acount the new coordinates
+        m_coordModel.setCoord(coord);
+    }
+    else if (iEvent->button() == Qt::RightButton)
+    {
+        // On a right click anywhere on the board, remove the arrow
+        m_coordModel.clear();
+    }
 }
 

Index: qt/board_widget.h
===================================================================
RCS file: /cvsroot/eliot/eliot/qt/board_widget.h,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -b -r1.5 -r1.6
--- qt/board_widget.h   30 Nov 2008 20:53:45 -0000      1.5
+++ qt/board_widget.h   19 Feb 2009 18:25:18 -0000      1.6
@@ -24,15 +24,17 @@
 #include <QtGui/QFrame>
 
 
-class PublicGame;
 class QTreeView;
+class PublicGame;
+class CoordModel;
+class Coord;
 
 class BoardWidget: public QFrame
 {
     Q_OBJECT;
 
 public:
-    explicit BoardWidget(QWidget *parent = 0);
+    explicit BoardWidget(CoordModel &iCoordModel, QWidget *parent = 0);
 
 public slots:
     void setGame(const PublicGame *iGame);
@@ -43,11 +45,19 @@
     virtual QSize sizeHint() const;
     /// Paint the board
     virtual void paintEvent(QPaintEvent *iEvent);
+    /// Catch mouse clicks on the board
+    virtual void mousePressEvent(QMouseEvent *iEvent);
+
+private slots:
+    void updateArrow(const Coord &iCoord);
 
 private:
     /// Encapsulated game, can be NULL
     const PublicGame *m_game;
 
+    /// Coordinates of the next word to play
+    CoordModel &m_coordModel;
+
     /// Define a few background colours
     //@{
     static const QColor EmptyColour;
@@ -59,6 +69,7 @@
     static const QColor PreviewColour;
     static const QColor NormalColour;
     static const QColor JokerColour;
+    static const QColor ArrowColour;
     //@}
 };
 

Index: qt/main_window.cpp
===================================================================
RCS file: /cvsroot/eliot/eliot/qt/main_window.cpp,v
retrieving revision 1.29
retrieving revision 1.30
diff -u -b -r1.29 -r1.30
--- qt/main_window.cpp  24 Jan 2009 17:44:57 -0000      1.29
+++ qt/main_window.cpp  19 Feb 2009 18:25:18 -0000      1.30
@@ -99,7 +99,7 @@
                      this, SLOT(updateStatusBar(const Dictionary*)));
 
     // Board
-    BoardWidget *boardWidget = new BoardWidget;
+    BoardWidget *boardWidget = new BoardWidget(m_coordModel);
     QObject::connect(this, SIGNAL(gameChanged(const PublicGame*)),
                      boardWidget, SLOT(setGame(const PublicGame*)));
     QObject::connect(this, SIGNAL(gameUpdated()),
@@ -134,7 +134,7 @@
 
     // Players racks
     m_ui.groupBoxPlayers->hide();
-    m_playersWidget = new PlayerTabWidget(NULL);
+    m_playersWidget = new PlayerTabWidget(m_coordModel, NULL);
     m_ui.groupBoxPlayers->layout()->addWidget(m_playersWidget);
     QObject::connect(this, SIGNAL(gameChangedNonConst(PublicGame*)),
                      m_playersWidget, SLOT(setGame(PublicGame*)));
@@ -765,7 +765,7 @@
     if (m_boardWindow == NULL)
     {
         // Create the window
-        BoardWidget *board = new BoardWidget(NULL);
+        BoardWidget *board = new BoardWidget(m_coordModel, NULL);
         board->setGame(m_game);
         m_boardWindow = new AuxWindow(*board, _q("Board"), "BoardWindow",
                                       m_actionWindowsBoard);

Index: qt/main_window.h
===================================================================
RCS file: /cvsroot/eliot/eliot/qt/main_window.h,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -b -r1.15 -r1.16
--- qt/main_window.h    14 Dec 2008 13:20:46 -0000      1.15
+++ qt/main_window.h    19 Feb 2009 18:25:19 -0000      1.16
@@ -24,6 +24,7 @@
 #include <QMainWindow>
 
 #include <ui/main_window.ui.h>
+#include "coord_model.h"
 
 
 class Dictionary;
@@ -139,9 +140,12 @@
     AuxWindow *m_dicToolsWindow;
     //@}
 
-    /// Label indicationg the name of the current dictionary
+    /// Label indicating the name of the current dictionary
     QLabel *m_dicNameLabel;
 
+    /// Model for the coordinates of the word to play
+    CoordModel m_coordModel;
+
     /// Save window state
     void writeSettings() const;
     /// Restore window state

Index: qt/player_widget.cpp
===================================================================
RCS file: /cvsroot/eliot/eliot/qt/player_widget.cpp,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -b -r1.14 -r1.15
--- qt/player_widget.cpp        14 Dec 2008 13:20:46 -0000      1.14
+++ qt/player_widget.cpp        19 Feb 2009 18:25:19 -0000      1.15
@@ -25,11 +25,13 @@
 
 #include "player_widget.h"
 #include "training_widget.h"
+#include "play_word_mediator.h"
 #include "qtcommon.h"
 #include "public_game.h"
 #include "player.h"
 #include "pldrack.h"
 #include "coord.h"
+#include "coord_model.h"
 #include "dic.h"
 #include "debug.h"
 
@@ -49,47 +51,22 @@
 };
 
 
-/// Validator used for the "play word" line edit
-class PlayWordValidator: public QValidator
-{
-public:
-    explicit PlayWordValidator(QObject *parent,
-                               const Dictionary &iDic);
-    virtual State validate(QString &input, int &pos) const;
-
-private:
-    const Dictionary &m_dic;
-};
-
-
-/// Validator used for the "coords" line edit
-class CoordsValidator: public QValidator
-{
-public:
-    explicit CoordsValidator(QObject *parent);
-    virtual State validate(QString &input, int &pos) const;
-};
-
-
-PlayerWidget::PlayerWidget(QWidget *parent, unsigned int iPlayerNb, PublicGame 
*iGame)
+PlayerWidget::PlayerWidget(QWidget *parent, CoordModel &iCoordModel,
+                           unsigned int iPlayerNb, PublicGame *iGame)
     : QWidget(parent), m_game(iGame), m_player(iPlayerNb)
 {
     setupUi(this);
-    lineEditPlay->setFocus();
-    // These strings cannot be in the .ui file, because of the newlines
-    lineEditPlay->setToolTip(_q("Enter the word to play (case-insensitive).\n"
-            "A joker from the rack must be written in parentheses.\n"
-            "E.g.: w(o)rd or W(O)RD"));
-    lineEditCoords->setToolTip(_q("Enter the coordinates of the word.\n"
-            "Specify the row before the column for horizontal words,\n"
-            "and the column before the row for vertical words.\n"
-            "E.g.: H4 or 4H"));
+
+    // Use the mediator
+    m_mediator = new PlayWordMediator(this, *lineEditPlay, *lineEditCoords,
+                                      *pushButtonPlay, iCoordModel, iGame);
+    QObject::connect(m_mediator, SIGNAL(gameUpdated()),
+                     this, SIGNAL(gameUpdated()));
+    QObject::connect(m_mediator, SIGNAL(notifyProblem(QString)),
+                     this, SIGNAL(notifyProblem(QString)));
 
     if (m_game)
     {
-        lineEditPlay->setValidator(new PlayWordValidator(this, 
m_game->getDic()));
-        lineEditCoords->setValidator(new CoordsValidator(this));
-
         // Do not allow messing with AI players
         if (!m_game->getPlayer(m_player).isHuman())
             setEnabled(false);
@@ -156,13 +133,6 @@
 }
 
 
-void PlayerWidget::on_lineEditPlay_textChanged()
-{
-    pushButtonPlay->setEnabled(lineEditPlay->hasAcceptableInput() &&
-                               lineEditCoords->hasAcceptableInput());
-}
-
-
 void PlayerWidget::on_lineEditChange_textChanged()
 {
     pushButtonChange->setEnabled(lineEditChange->hasAcceptableInput() &&
@@ -171,93 +141,6 @@
 }
 
 
-void PlayerWidget::on_lineEditPlay_returnPressed()
-{
-    if (!lineEditPlay->hasAcceptableInput() ||
-        !lineEditCoords->hasAcceptableInput())
-        return;
-
-    // Convert the jokers to lowercase
-    QString word = lineEditPlay->text().toUpper();
-    int pos;
-    while ((pos = word.indexOf('(')) != -1)
-    {
-        if (word.size() < pos + 3 || word[pos + 2] != ')' ||
-            !m_game->getDic().validateLetters(qtw(QString(word[pos + 1]))))
-        {
-            // Bug in validate()!
-            // This should never happen
-            QString msg = _q("Cannot play word: misplaced parentheses");
-            emit notifyProblem(msg);
-            break;
-        }
-        else
-        {
-            QChar chr = word[pos + 1].toLower();
-            word.remove(pos, 3);
-            word.insert(pos, chr);
-        }
-    }
-
-    QString coords = lineEditCoords->text();
-    int res = m_game->play(qtw(word), qtw(coords));
-    if (res == 0)
-    {
-        emit gameUpdated();
-        lineEditPlay->setFocus();
-    }
-    else
-    {
-        // Try to be as explicit as possible concerning the error
-        QString msg = _q("Cannot play '%1' at position '%2':\n")
-            .arg(lineEditPlay->text()).arg(coords);
-        switch (res)
-        {
-            case 1:
-                msg += _q("Some letters are not valid for the current 
dictionary");
-                break;
-            case 2:
-                msg += _q("Invalid coordinates");
-                break;
-            case 3:
-                msg += _q("The word does not exist");
-                break;
-            case 4:
-                msg += _q("The rack doesn't contain the letters needed to play 
this word");
-                break;
-            case 5:
-                msg += _q("The word is part of a longer one");
-                break;
-            case 6:
-                msg += _q("The word tries to replace an existing letter");
-                break;
-            case 7:
-                msg += _q("An orthogonal word is not valid");
-                break;
-            case 8:
-                msg += _q("The word is already present on the board at these 
coordinates");
-                break;
-            case 9:
-                msg += _q("A word cannot be isolated (not connected to the 
placed words)");
-                break;
-            case 10:
-                msg += _q("The first word of the game must be horizontal");
-                break;
-            case 11:
-                msg += _q("The first word of the game must cover the H8 
square");
-                break;
-            case 12:
-                msg += _q("The word is going out of the board");
-                break;
-            default:
-                msg += _q("Incorrect or misplaced word (%1)").arg(1);
-        }
-        // FIXME: the error is too generic
-        emit notifyProblem(msg);
-    }
-}
-
-
 void PlayerWidget::on_lineEditChange_returnPressed()
 {
     ASSERT(m_game->getMode() == PublicGame::kFREEGAME,
@@ -308,76 +191,8 @@
 
 
 
-PlayWordValidator::PlayWordValidator(QObject *parent,
-                                     const Dictionary &iDic)
-    : QValidator(parent), m_dic(iDic)
-{
-}
-
-
-QValidator::State PlayWordValidator::validate(QString &input, int &) const
-{
-    if (input == "")
-        return Intermediate;
-
-    QString copy(input);
-    // Strip parentheses
-    copy.remove('(');
-    copy.remove(')');
-    // The string is invalid if it contains characters not present
-    // in the dictionary
-    if (!m_dic.validateLetters(qtw(copy)) || copy.contains('?'))
-        return Invalid;
-
-    // Check the parentheses pairs
-    copy = input;
-    int pos;
-    while ((pos = copy.indexOf('(')) != -1)
-    {
-        if (copy.size() < pos + 3 || copy[pos + 2] != ')' ||
-            !m_dic.validateLetters(qtw(QString(copy[pos + 1]))))
-        {
-            return Intermediate;
-        }
-        else
-        {
-            copy.remove(pos, 3);
-        }
-    }
-    if (copy.indexOf(')') != -1)
-        return Intermediate;
-
-    return Acceptable;
-}
-
-
-
-CoordsValidator::CoordsValidator(QObject *parent)
-    : QValidator(parent)
-{
-}
-
-
-QValidator::State CoordsValidator::validate(QString &input, int &) const
-{
-    // Only authorize characters part of a valid coordinate
-    wstring copy = qtw(input.toUpper());
-    wstring authorized = L"ABCDEFGHIJKLMNO1234567890";
-    if (copy.find_first_not_of(authorized) != wstring::npos)
-        return Invalid;
-
-    // Check coordinates
-    Coord c(qtw(input));
-    if (!c.isValid())
-        return Intermediate;
-
-    return Acceptable;
-}
-
-
-
-PlayerTabWidget::PlayerTabWidget(QWidget *parent)
-    : QTabWidget(parent)
+PlayerTabWidget::PlayerTabWidget(CoordModel &iCoordModel, QWidget *parent)
+    : QTabWidget(parent), m_coordModel(iCoordModel)
 {
     QObject::connect(this, SIGNAL(currentChanged(int)),
                      this, SLOT(changeCurrentPlayer(int)));
@@ -405,8 +220,7 @@
         if (iGame->getMode() == PublicGame::kTRAINING)
         {
             const Player &player = iGame->getPlayer(0);
-            TrainingWidget *trWidget = new TrainingWidget;
-            trWidget->setGame(iGame);
+            TrainingWidget *trWidget = new TrainingWidget(NULL, m_coordModel, 
iGame);
             QObject::connect(this, SIGNAL(refreshSignal()),
                              trWidget, SLOT(refresh()));
             // Forward signals to the outside
@@ -426,7 +240,7 @@
             for (unsigned int i = 0; i < iGame->getNbPlayers(); ++i)
             {
                 const Player &player = iGame->getPlayer(i);
-                PlayerWidget *p = new PlayerWidget(NULL, i, iGame);
+                PlayerWidget *p = new PlayerWidget(NULL, m_coordModel, i, 
iGame);
                 QObject::connect(this, SIGNAL(refreshSignal()), p, 
SLOT(refresh()));
                 // Forward signals to the outside
                 QObject::connect(p, SIGNAL(notifyProblem(QString)),
@@ -460,6 +274,8 @@
     if (m_game == NULL)
         return;
 
+    m_coordModel.clear();
+
     // Change the active player when the active tab changes
     // (only in duplicate mode)
     if (m_game->getMode() == PublicGame::kDUPLICATE &&

Index: qt/player_widget.h
===================================================================
RCS file: /cvsroot/eliot/eliot/qt/player_widget.h,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -b -r1.9 -r1.10
--- qt/player_widget.h  14 Dec 2008 13:20:47 -0000      1.9
+++ qt/player_widget.h  19 Feb 2009 18:25:19 -0000      1.10
@@ -18,8 +18,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *****************************************************************************/
 
-#ifndef RACK_WIDGET_H_
-#define RACK_WIDGET_H_
+#ifndef PLAYER_WIDGET_H_
+#define PLAYER_WIDGET_H_
 
 #include <QtGui/QWidget>
 #include <QtGui/QTabWidget>
@@ -28,13 +28,17 @@
 
 class QLineEdit;
 class PublicGame;
+class PlayWordMediator;
+class CoordModel;
+class Coord;
 
 class PlayerWidget: public QWidget, private Ui::PlayerWidget
 {
     Q_OBJECT;
 
 public:
-    explicit PlayerWidget(QWidget *parent = 0,
+    explicit PlayerWidget(QWidget *parent,
+                          CoordModel &iCoordModel,
                           unsigned int iPlayerNb = 0,
                           PublicGame *iGame = NULL);
 
@@ -51,20 +55,18 @@
 
 private slots:
     void on_pushButtonShuffle_clicked();
-    void on_pushButtonPlay_clicked() { on_lineEditPlay_returnPressed(); }
     void on_pushButtonChange_clicked() { on_lineEditChange_returnPressed(); }
     void on_pushButtonPass_clicked() { on_lineEditChange_returnPressed(); }
-    void on_lineEditPlay_textChanged();
-    void on_lineEditCoords_textChanged() { on_lineEditPlay_textChanged(); }
     void on_lineEditChange_textChanged();
-    void on_lineEditPlay_returnPressed();
-    void on_lineEditCoords_returnPressed() { on_lineEditPlay_returnPressed(); }
     void on_lineEditChange_returnPressed();
 
 private:
     /// Encapsulated game, can be NULL
     PublicGame *m_game;
 
+    /// Mediator for the "play word" controls
+    PlayWordMediator *m_mediator;
+
     /// Encapsulated player, valid iff m_game is not NULL
     unsigned int m_player;
 
@@ -76,7 +78,7 @@
     Q_OBJECT;
 
 public:
-    explicit PlayerTabWidget(QWidget *parent = 0);
+    explicit PlayerTabWidget(CoordModel &iCoordModel, QWidget *parent = 0);
 
 public slots:
     void setGame(PublicGame *iGame);
@@ -95,6 +97,9 @@
 private:
     /// Encapsulated game, can be NULL
     PublicGame *m_game;
+
+    /// Model for the word coordinates
+    CoordModel &m_coordModel;
 };
 
 #endif

Index: qt/training_widget.cpp
===================================================================
RCS file: /cvsroot/eliot/eliot/qt/training_widget.cpp,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -b -r1.9 -r1.10
--- qt/training_widget.cpp      24 Jan 2009 10:28:21 -0000      1.9
+++ qt/training_widget.cpp      19 Feb 2009 18:25:20 -0000      1.10
@@ -23,6 +23,8 @@
 
 #include "training_widget.h"
 #include "qtcommon.h"
+#include "play_word_mediator.h"
+
 #include "dic.h"
 #include "bag.h"
 #include "public_game.h"
@@ -37,21 +39,27 @@
 class RackValidator: public QValidator
 {
 public:
-    explicit RackValidator(QObject *parent);
+    explicit RackValidator(QObject *parent, const Bag *iBag);
     virtual State validate(QString &input, int &pos) const;
 
-    void setBag(const Bag *iBag) { m_bag = iBag; }
-
 private:
     const Bag *m_bag;
 };
 
 
-TrainingWidget::TrainingWidget(QWidget *parent)
-    : QWidget(parent), m_game(NULL)
+TrainingWidget::TrainingWidget(QWidget *parent, CoordModel &iCoordModel, 
PublicGame *iGame)
+    : QWidget(parent), m_game(iGame)
 {
     setupUi(this);
 
+    // Use the mediator
+    m_mediator = new PlayWordMediator(this, *lineEditPlay, *lineEditCoords,
+                                      *pushButtonPlay, iCoordModel, m_game);
+    QObject::connect(m_mediator, SIGNAL(gameUpdated()),
+                     this, SIGNAL(gameUpdated()));
+    QObject::connect(m_mediator, SIGNAL(notifyProblem(QString)),
+                     this, SIGNAL(notifyProblem(QString)));
+
     // Associate the model to the view
     m_model = new QStandardItemModel(this);
     treeViewResults->setModel(m_model);
@@ -76,8 +84,9 @@
                      this,
                      SLOT(showPreview(const QItemSelection&, const 
QItemSelection&)));
 
-    m_validator = new RackValidator(this);
-    lineEditRack->setValidator(m_validator);
+    if (m_game)
+        lineEditRack->setValidator(new RackValidator(this, &m_game->getBag()));
+
     // Notify that the rack changed
     QObject::connect(lineEditRack, SIGNAL(textChanged(const QString&)),
                      this, SIGNAL(rackUpdated(const QString&)));
@@ -86,17 +95,6 @@
 }
 
 
-void TrainingWidget::setGame(PublicGame *iGame)
-{
-    m_game = iGame;
-    if (m_game != NULL)
-        m_validator->setBag(&m_game->getBag());
-    else
-        m_validator->setBag(NULL);
-    refresh();
-}
-
-
 void TrainingWidget::refresh()
 {
     updateModel();
@@ -115,6 +113,8 @@
         // Update the rack only if it is needed, to avoid losing cursor 
position
         if (qfw(rack) != lineEditRack->text())
             lineEditRack->setText(qfw(rack));
+        lineEditPlay->clear();
+        lineEditCoords->clear();
         lineEditRack->setEnabled(true);
         pushButtonRack->setEnabled(true);
         pushButtonComplement->setEnabled(true);
@@ -171,9 +171,9 @@
 void TrainingWidget::enablePlayButton(const QItemSelection &iSelected,
                                       const QItemSelection &)
 {
-    // Enable the "Play" button iff at least one line in the tree view
-    // is selected
-    pushButtonPlay->setEnabled(!iSelected.indexes().empty());
+    // Enable the "Play selected" button iff at least one line
+    // in the tree view is selected
+    pushButtonPlaySelected->setEnabled(!iSelected.indexes().empty());
 }
 
 
@@ -252,7 +252,7 @@
 }
 
 
-void TrainingWidget::on_pushButtonPlay_clicked()
+void TrainingWidget::on_pushButtonPlaySelected_clicked()
 {
     QModelIndexList indexList = 
treeViewResults->selectionModel()->selectedIndexes();
     if (indexList.empty())
@@ -281,8 +281,8 @@
 
 
 
-RackValidator::RackValidator(QObject *parent)
-    : QValidator(parent)
+RackValidator::RackValidator(QObject *parent, const Bag *iBag)
+    : QValidator(parent), m_bag(iBag)
 {
 }
 

Index: qt/training_widget.h
===================================================================
RCS file: /cvsroot/eliot/eliot/qt/training_widget.h,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -b -r1.5 -r1.6
--- qt/training_widget.h        14 Dec 2008 13:20:49 -0000      1.5
+++ qt/training_widget.h        19 Feb 2009 18:25:20 -0000      1.6
@@ -25,20 +25,20 @@
 #include "ui/training_widget.ui.h"
 
 
-class PublicGame;
-class RackValidator;
 class QStandardItemModel;
 class QString;
+class CoordModel;
+class PlayWordMediator;
+class PublicGame;
 
 class TrainingWidget: public QWidget, private Ui::TrainingWidget
 {
     Q_OBJECT;
 
 public:
-    explicit TrainingWidget(QWidget *parent = 0);
+    explicit TrainingWidget(QWidget *parent, CoordModel &iCoordModel, 
PublicGame *iGame);
 
 public slots:
-    void setGame(PublicGame *iGame);
     void refresh();
 
 signals:
@@ -60,7 +60,7 @@
     void on_pushButtonRack_clicked();
     void on_pushButtonComplement_clicked();
     void on_pushButtonSearch_clicked();
-    void on_pushButtonPlay_clicked();
+    void on_pushButtonPlaySelected_clicked();
     void on_treeViewResults_doubleClicked(const QModelIndex &iIndex);
 
 private:
@@ -70,8 +70,8 @@
     /// Model of the search results
     QStandardItemModel *m_model;
 
-    /// Validator for the rack edition
-    RackValidator *m_validator;
+    /// Mediator for the "play word" controls
+    PlayWordMediator *m_mediator;
 
     /// Force synchronizing the model with the contents of the search results
     void updateModel();

Index: qt/ui/player_widget.ui
===================================================================
RCS file: /cvsroot/eliot/eliot/qt/ui/player_widget.ui,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -b -r1.4 -r1.5
--- qt/ui/player_widget.ui      14 Sep 2008 17:56:19 -0000      1.4
+++ qt/ui/player_widget.ui      19 Feb 2009 18:25:20 -0000      1.5
@@ -6,7 +6,7 @@
     <x>0</x>
     <y>0</y>
     <width>460</width>
-    <height>148</height>
+    <height>117</height>
    </rect>
   </property>
   <layout class="QGridLayout" name="gridLayout" >
@@ -31,6 +31,44 @@
      </property>
     </widget>
    </item>
+   <item row="2" column="0" >
+    <widget class="QLabel" name="labelChange" >
+     <property name="text" >
+      <string>_("Change letters:")</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1" >
+    <widget class="QLineEdit" name="lineEditChange" >
+     <property name="toolTip" >
+      <string>_("Enter the letters you want to change")</string>
+     </property>
+     <property name="text" >
+      <string>dummy</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="2" colspan="2" >
+    <widget class="QPushButton" name="pushButtonChange" >
+     <property name="text" >
+      <string>_("Change")</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="4" >
+    <widget class="QPushButton" name="pushButtonPass" >
+     <property name="text" >
+      <string>_("Pass")</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="2" >
+    <widget class="QLabel" name="label_3" >
+     <property name="text" >
+      <string>_("Ref.:")</string>
+     </property>
+    </widget>
+   </item>
    <item row="1" column="0" >
     <widget class="QLabel" name="label_2" >
      <property name="text" >
@@ -53,6 +91,13 @@
      </property>
     </widget>
    </item>
+   <item row="1" column="4" >
+    <widget class="QPushButton" name="pushButtonPlay" >
+     <property name="text" >
+      <string>_("Play")</string>
+     </property>
+    </widget>
+   </item>
    <item row="1" column="3" >
     <widget class="QLineEdit" name="lineEditCoords" >
      <property name="sizePolicy" >
@@ -78,51 +123,6 @@
      </property>
     </widget>
    </item>
-   <item row="1" column="4" >
-    <widget class="QPushButton" name="pushButtonPlay" >
-     <property name="text" >
-      <string>_("Play")</string>
-     </property>
-    </widget>
-   </item>
-   <item row="2" column="0" >
-    <widget class="QLabel" name="labelChange" >
-     <property name="text" >
-      <string>_("Change letters:")</string>
-     </property>
-    </widget>
-   </item>
-   <item row="2" column="1" >
-    <widget class="QLineEdit" name="lineEditChange" >
-     <property name="toolTip" >
-      <string>_("Enter the letters you want to change")</string>
-     </property>
-     <property name="text" >
-      <string>dummy</string>
-     </property>
-    </widget>
-   </item>
-   <item row="2" column="2" colspan="2" >
-    <widget class="QPushButton" name="pushButtonChange" >
-     <property name="text" >
-      <string>_("Change")</string>
-     </property>
-    </widget>
-   </item>
-   <item row="2" column="4" >
-    <widget class="QPushButton" name="pushButtonPass" >
-     <property name="text" >
-      <string>_("Pass")</string>
-     </property>
-    </widget>
-   </item>
-   <item row="1" column="2" >
-    <widget class="QLabel" name="label_3" >
-     <property name="text" >
-      <string>_("Ref.:")</string>
-     </property>
-    </widget>
-   </item>
   </layout>
  </widget>
  <resources/>

Index: qt/ui/training_widget.ui
===================================================================
RCS file: /cvsroot/eliot/eliot/qt/ui/training_widget.ui,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -b -r1.3 -r1.4
--- qt/ui/training_widget.ui    31 Aug 2008 11:48:22 -0000      1.3
+++ qt/ui/training_widget.ui    19 Feb 2009 18:25:20 -0000      1.4
@@ -5,14 +5,14 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>415</width>
-    <height>371</height>
+    <width>511</width>
+    <height>300</height>
    </rect>
   </property>
   <property name="windowTitle" >
    <string>Form</string>
   </property>
-  <layout class="QVBoxLayout" >
+  <layout class="QVBoxLayout" name="verticalLayout" >
    <item>
     <layout class="QHBoxLayout" >
      <item>
@@ -23,7 +23,81 @@
       </widget>
      </item>
      <item>
-      <widget class="QLineEdit" name="lineEditRack" />
+      <widget class="QLineEdit" name="lineEditRack" >
+       <property name="minimumSize" >
+        <size>
+         <width>60</width>
+         <height>0</height>
+        </size>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_2" >
+       <property name="text" >
+        <string>_("Play a word:")</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="lineEditPlay" >
+       <property name="minimumSize" >
+        <size>
+         <width>60</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="toolTip" >
+        <string>_("Enter the word to play (case-insensitive).
+A joker from the rack must be written in parentheses.
+E.g.: w(o)rd or W(O)RD")</string>
+       </property>
+       <property name="statusTip" >
+        <string>_("Enter the word to play (case-insensitive). A joker from the 
rack must be written in parentheses. E.g.: w(o)rd or W(O)RD")</string>
+       </property>
+       <property name="text" >
+        <string>dummy</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_3" >
+       <property name="text" >
+        <string>_("Ref.:")</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="lineEditCoords" >
+       <property name="sizePolicy" >
+        <sizepolicy vsizetype="Fixed" hsizetype="Fixed" >
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="maximumSize" >
+        <size>
+         <width>45</width>
+         <height>16777215</height>
+        </size>
+       </property>
+       <property name="toolTip" >
+        <string>_("Enter the coordinates of the word.
+Specify the row before the column for horizontal words,
+ and the column before the row for vertical words.
+E.g.: H4 or 4H")</string>
+       </property>
+       <property name="statusTip" >
+        <string>_("Enter the coordinates of the word. Specify the row before 
the column for horizontal words, and the column before the row for vertical 
words. E.g.: H4 or 4H")</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="pushButtonPlay" >
+       <property name="text" >
+        <string>_("Play")</string>
+       </property>
+      </widget>
      </item>
     </layout>
    </item>
@@ -54,12 +128,12 @@
       </widget>
      </item>
      <item>
-      <widget class="QPushButton" name="pushButtonPlay" >
+      <widget class="QPushButton" name="pushButtonPlaySelected" >
        <property name="enabled" >
         <bool>false</bool>
        </property>
        <property name="text" >
-        <string>_("Play")</string>
+        <string>_("Play selected")</string>
        </property>
       </widget>
      </item>
@@ -82,6 +156,7 @@
        </widget>
       </item>
      </layout>
+     <zorder>treeViewResults</zorder>
     </widget>
    </item>
   </layout>

Index: qt/coord_model.cpp
===================================================================
RCS file: qt/coord_model.cpp
diff -N qt/coord_model.cpp
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ qt/coord_model.cpp  19 Feb 2009 18:25:18 -0000      1.1
@@ -0,0 +1,36 @@
+/*****************************************************************************
+ * Eliot
+ * Copyright (C) 2009 Olivier Teulière
+ * Authors: Olivier Teulière <ipkiss @@ gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *****************************************************************************/
+
+#include "coord_model.h"
+
+
+void CoordModel::setCoord(const Coord &iCoord)
+{
+    m_prevCoord = m_currCoord;
+    m_currCoord = iCoord;
+    emit coordChanged(iCoord);
+}
+
+
+void CoordModel::clear()
+{
+    setCoord(Coord());
+}
+

Index: qt/coord_model.h
===================================================================
RCS file: qt/coord_model.h
diff -N qt/coord_model.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ qt/coord_model.h    19 Feb 2009 18:25:18 -0000      1.1
@@ -0,0 +1,48 @@
+/*****************************************************************************
+ * Eliot
+ * Copyright (C) 2009 Olivier Teulière
+ * Authors: Olivier Teulière <ipkiss @@ gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *****************************************************************************/
+
+#ifndef COORD_MODEL_H_
+#define COORD_MODEL_H_
+
+#include <QObject>
+#include "coord.h"
+
+
+class CoordModel: public QObject
+{
+    Q_OBJECT;
+
+public:
+    void setCoord(const Coord &iCoord);
+    void clear();
+
+    const Coord &getCoord() const { return m_currCoord; }
+    const Coord &getPrevCoord() const { return m_prevCoord; }
+
+signals:
+    void coordChanged(const Coord &iNewCoord);
+
+private:
+    Coord m_currCoord;
+    Coord m_prevCoord;
+};
+
+#endif
+

Index: qt/play_word_mediator.cpp
===================================================================
RCS file: qt/play_word_mediator.cpp
diff -N qt/play_word_mediator.cpp
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ qt/play_word_mediator.cpp   19 Feb 2009 18:25:19 -0000      1.1
@@ -0,0 +1,278 @@
+/*****************************************************************************
+ * Eliot
+ * Copyright (C) 2009 Olivier Teulière
+ * Authors: Olivier Teulière <ipkiss @@ gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *****************************************************************************/
+
+#include <QtGui/QLineEdit>
+#include <QtGui/QPushButton>
+
+#include "play_word_mediator.h"
+#include "coord_model.h"
+#include "qtcommon.h"
+
+#include "public_game.h"
+#include "coord.h"
+#include "dic.h"
+#include "debug.h"
+
+
+/// Validator used for the "play word" line edit
+class PlayWordValidator: public QValidator
+{
+public:
+    explicit PlayWordValidator(QObject *parent,
+                               const Dictionary &iDic);
+    virtual State validate(QString &input, int &pos) const;
+
+private:
+    const Dictionary &m_dic;
+};
+
+
+/// Validator used for the "coords" line edit
+class CoordsValidator: public QValidator
+{
+public:
+    explicit CoordsValidator(QObject *parent);
+    virtual State validate(QString &input, int &pos) const;
+};
+
+
+
+PlayWordMediator::PlayWordMediator(QObject *parent, QLineEdit &iEditPlay,
+                                   QLineEdit &iEditCoord, QPushButton 
&iButtonPlay,
+                                   CoordModel &iCoordModel, PublicGame *iGame)
+    : QObject(parent), m_game(iGame), m_lineEditPlay(iEditPlay),
+    m_lineEditCoord(iEditCoord), m_pushButtonPlay(iButtonPlay),
+    m_coordModel(iCoordModel)
+{
+    m_lineEditPlay.setFocus();
+    // These strings cannot be in the .ui file, because of the newlines
+    m_lineEditPlay.setToolTip(_q("Enter the word to play (case-insensitive).\n"
+            "A joker from the rack must be written in parentheses.\n"
+            "E.g.: w(o)rd or W(O)RD"));
+    m_lineEditCoord.setToolTip(_q("Enter the coordinates of the word.\n"
+            "Specify the row before the column for horizontal words,\n"
+            "and the column before the row for vertical words.\n"
+            "E.g.: H4 or 4H"));
+
+    /// Set validators;
+    if (m_game)
+    {
+        m_lineEditPlay.setValidator(new PlayWordValidator(this, 
m_game->getDic()));
+        m_lineEditCoord.setValidator(new CoordsValidator(this));
+    }
+
+    // Set all the connections
+    QObject::connect(&m_lineEditPlay, SIGNAL(textChanged(const QString&)),
+                     this, SLOT(lineEditPlay_textChanged()));
+    QObject::connect(&m_lineEditPlay, SIGNAL(returnPressed()),
+                     this, SLOT(lineEditPlay_returnPressed()));
+    QObject::connect(&m_lineEditCoord, SIGNAL(textChanged(const QString&)),
+                     this, SLOT(lineEditCoord_textChanged(const QString&)));
+    QObject::connect(&m_lineEditCoord, SIGNAL(returnPressed()),
+                     this, SLOT(lineEditCoord_returnPressed()));
+    QObject::connect(&m_pushButtonPlay, SIGNAL(clicked()),
+                     this, SLOT(pushButtonPlay_clicked()));
+    QObject::connect(&m_coordModel, SIGNAL(coordChanged(const Coord&)),
+                     this, SLOT(updateCoord(const Coord&)));
+}
+
+
+void PlayWordMediator::lineEditPlay_textChanged()
+{
+    m_pushButtonPlay.setEnabled(m_lineEditPlay.hasAcceptableInput() &&
+                                m_lineEditCoord.hasAcceptableInput());
+}
+
+
+void PlayWordMediator::lineEditPlay_returnPressed()
+{
+    if (!m_lineEditPlay.hasAcceptableInput() ||
+        !m_lineEditCoord.hasAcceptableInput())
+        return;
+
+    // Convert the jokers to lowercase
+    QString word = m_lineEditPlay.text().toUpper();
+    int pos;
+    while ((pos = word.indexOf('(')) != -1)
+    {
+        if (word.size() < pos + 3 || word[pos + 2] != ')' ||
+            !m_game->getDic().validateLetters(qtw(QString(word[pos + 1]))))
+        {
+            // Bug in validate()!
+            // This should never happen
+            QString msg = _q("Cannot play word: misplaced parentheses");
+            emit notifyProblem(msg);
+            break;
+        }
+        else
+        {
+            QChar chr = word[pos + 1].toLower();
+            word.remove(pos, 3);
+            word.insert(pos, chr);
+        }
+    }
+
+    QString coords = m_lineEditCoord.text();
+    int res = m_game->play(qtw(word), qtw(coords));
+    if (res == 0)
+    {
+        emit gameUpdated();
+        m_lineEditPlay.setFocus();
+    }
+    else
+    {
+        // Try to be as explicit as possible concerning the error
+        QString msg = _q("Cannot play '%1' at position '%2':\n")
+            .arg(m_lineEditPlay.text()).arg(coords);
+        switch (res)
+        {
+            case 1:
+                msg += _q("Some letters are not valid for the current 
dictionary");
+                break;
+            case 2:
+                msg += _q("Invalid coordinates");
+                break;
+            case 3:
+                msg += _q("The word does not exist");
+                break;
+            case 4:
+                msg += _q("The rack doesn't contain the letters needed to play 
this word");
+                break;
+            case 5:
+                msg += _q("The word is part of a longer one");
+                break;
+            case 6:
+                msg += _q("The word tries to replace an existing letter");
+                break;
+            case 7:
+                msg += _q("An orthogonal word is not valid");
+                break;
+            case 8:
+                msg += _q("The word is already present on the board at these 
coordinates");
+                break;
+            case 9:
+                msg += _q("A word cannot be isolated (not connected to the 
placed words)");
+                break;
+            case 10:
+                msg += _q("The first word of the game must be horizontal");
+                break;
+            case 11:
+                msg += _q("The first word of the game must cover the H8 
square");
+                break;
+            case 12:
+                msg += _q("The word is going out of the board");
+                break;
+            default:
+                msg += _q("Incorrect or misplaced word (%1)").arg(1);
+        }
+        // FIXME: the error is too generic
+        emit notifyProblem(msg);
+    }
+}
+
+
+void PlayWordMediator::lineEditCoord_textChanged(const QString &iText)
+{
+    Coord c(qtw(iText));
+    if (!(m_coordModel.getCoord() == c))
+        m_coordModel.setCoord(Coord(qtw(iText)));
+    lineEditPlay_textChanged();
+}
+
+
+void PlayWordMediator::updateCoord(const Coord &iCoord)
+{
+    if (iCoord.isValid() && m_lineEditCoord.text() != qfw(iCoord.toString()))
+        m_lineEditCoord.setText(qfw(iCoord.toString()));
+    else if (!iCoord.isValid() && m_lineEditCoord.hasAcceptableInput())
+        m_lineEditCoord.setText("");
+    lineEditPlay_textChanged();
+}
+
+
+// ------ Validators ------
+
+PlayWordValidator::PlayWordValidator(QObject *parent,
+                                     const Dictionary &iDic)
+    : QValidator(parent), m_dic(iDic)
+{
+}
+
+
+QValidator::State PlayWordValidator::validate(QString &input, int &) const
+{
+    if (input == "")
+        return Intermediate;
+
+    QString copy(input);
+    // Strip parentheses
+    copy.remove('(');
+    copy.remove(')');
+    // The string is invalid if it contains characters not present
+    // in the dictionary
+    if (!m_dic.validateLetters(qtw(copy)) || copy.contains('?'))
+        return Invalid;
+
+    // Check the parentheses pairs
+    copy = input;
+    int pos;
+    while ((pos = copy.indexOf('(')) != -1)
+    {
+        if (copy.size() < pos + 3 || copy[pos + 2] != ')' ||
+            !m_dic.validateLetters(qtw(QString(copy[pos + 1]))))
+        {
+            return Intermediate;
+        }
+        else
+        {
+            copy.remove(pos, 3);
+        }
+    }
+    if (copy.indexOf(')') != -1)
+        return Intermediate;
+
+    return Acceptable;
+}
+
+
+
+CoordsValidator::CoordsValidator(QObject *parent)
+    : QValidator(parent)
+{
+}
+
+
+QValidator::State CoordsValidator::validate(QString &input, int &) const
+{
+    // Only authorize characters part of a valid coordinate
+    wstring copy = qtw(input.toUpper());
+    wstring authorized = L"ABCDEFGHIJKLMNO1234567890";
+    if (copy.find_first_not_of(authorized) != wstring::npos)
+        return Invalid;
+
+    // Check coordinates
+    Coord c(qtw(input));
+    if (!c.isValid())
+        return Intermediate;
+
+    return Acceptable;
+}
+
+

Index: qt/play_word_mediator.h
===================================================================
RCS file: qt/play_word_mediator.h
diff -N qt/play_word_mediator.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ qt/play_word_mediator.h     19 Feb 2009 18:25:19 -0000      1.1
@@ -0,0 +1,77 @@
+/*****************************************************************************
+ * Eliot
+ * Copyright (C) 2008 Olivier Teulière
+ * Authors: Olivier Teulière <ipkiss @@ gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *****************************************************************************/
+
+#ifndef VALIDATORS_H_
+#define VALIDATORS_H_
+
+#include <QtGui/QValidator>
+
+
+class QLineEdit;
+class QPushButton;
+class PublicGame;
+class Coord;
+class CoordModel;
+class Dictionary;
+
+/**
+ * Mediator handling the connections between the controls used to play a word.
+ * The controls are:
+ *  - a QLineEdit for the played word
+ *  - a QLineEdit for the coordinates
+ *  - a Play button to really play the word
+ *
+ * All the logic between these controls is handled by the mediator, as long as
+ * it is alive (same lifetime as the controls themselves).
+ * Note: this class is not a graphical widget, because it would cause problems
+ * to place the controls exactly where wanted in the QGridLayout of the
+ * PlayerWidget class...
+ */
+class PlayWordMediator: public QObject
+{
+    Q_OBJECT;
+
+public:
+    PlayWordMediator(QObject *parent, QLineEdit &iEditWord,
+                     QLineEdit &iEditCoord, QPushButton &iButtonPlay,
+                     CoordModel &iCoordModel, PublicGame *iGame);
+
+signals:
+    void gameUpdated();
+    void notifyProblem(QString iMsg);
+
+private slots:
+    void lineEditPlay_textChanged();
+    void lineEditPlay_returnPressed();
+    void lineEditCoord_textChanged(const QString &iText);
+    void lineEditCoord_returnPressed() { lineEditPlay_returnPressed(); }
+    void pushButtonPlay_clicked() { lineEditPlay_returnPressed(); }
+    void updateCoord(const Coord &iNewCoord);
+
+private:
+    PublicGame *m_game;
+    QLineEdit &m_lineEditPlay;
+    QLineEdit &m_lineEditCoord;
+    QPushButton &m_pushButtonPlay;
+    CoordModel &m_coordModel;
+};
+
+#endif
+




reply via email to

[Prev in Thread] Current Thread [Next in Thread]