diff --git a/Makefile.am b/Makefile.am index 40db2b9..985d30f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = intl dic game utils wxwin po +SUBDIRS = intl dic game utils wxwin qt po ACLOCAL_AMFLAGS = -I m4 diff --git a/configure.in b/configure.in index b87f29c..797dd91 100644 --- a/configure.in +++ b/configure.in @@ -21,6 +21,7 @@ AC_GNU_SOURCE AC_PROG_CXX AC_PROG_MAKE_SET AC_PROG_RANLIB +PKG_PROG_PKG_CONFIG AC_PROG_YACC if test "$YACC" = yacc ; then @@ -153,6 +154,21 @@ else fi AM_CONDITIONAL([BUILD_NCURSES], [test "${want_ncurses}" = "1"]) +dnl check for Qt +AC_ARG_ENABLE([qt],AC_HELP_STRING([--enable-qt], + [Qt interface support (default disabled)])) +qt_ok=0 +AS_IF([test "${enable_qt}" = "yes"], + [PKG_CHECK_MODULES(QT, [QtCore QtGui >= 4.2.0], + [qt_ok=1 + AC_PATH_PROG(MOC, moc, moc,`$PKG_CONFIG --variable=exec_prefix QtCore`/bin) + AC_PATH_PROG(UIC, uic, uic,`$PKG_CONFIG --variable=exec_prefix QtCore`/bin) + ], + [AC_MSG_ERROR([Could not find Qt on your system (using pkg-config)])] + ) + ]) +AM_CONDITIONAL([BUILD_QT], [test "${qt_ok}" = "1"]) + dnl Enable/disable text version AC_ARG_ENABLE([text],AC_HELP_STRING([--enable-text],[text interface support (default enabled)])) AM_CONDITIONAL([BUILD_TEXT], [test "${enable_text}" != "no"]) @@ -192,7 +208,8 @@ dnl -------------------------------------------------------------- AC_CONFIG_FILES(intl/Makefile po/Makefile.in dic/Makefile game/Makefile -wxwin/Makefile utils/Makefile +wxwin/Makefile +qt/Makefile Makefile) AC_OUTPUT diff --git a/po/Makevars b/po/Makevars index 964202f..50b10c0 100644 --- a/po/Makevars +++ b/po/Makevars @@ -8,7 +8,7 @@ subdir = po top_builddir = .. # These options get passed to xgettext. -XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ +XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ --keyword=_q --language=C++ # This is the copyright holder that gets inserted into the header of the # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding diff --git a/po/POTFILES.in b/po/POTFILES.in index 9c6c2fc..3419bff 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,72 +1,92 @@ -./dic/automaton.cpp -./dic/automaton.h -./dic/header.cpp -./dic/header.h -./dic/dic.cpp -./dic/dic.h -./dic/dic_internals.h -./dic/dic_search.cpp -./dic/hashtable.cpp -./dic/hashtable.h -./dic/regexp.cpp -./dic/regexp.h -./dic/tile.cpp -./dic/tile.h -./dic/compdic.cpp -./dic/listdic.cpp -./dic/regexpmain.cpp -./game/bag.cpp -./game/bag.h -./game/board.cpp -./game/board.h -./game/board_cross.cpp -./game/board_search.cpp -./game/cross.cpp -./game/cross.h -./game/debug.h -./game/duplicate.cpp -./game/duplicate.h -./game/freegame.cpp -./game/freegame.h -./game/game.cpp -./game/game.h -./game/game_io.cpp -./game/history.cpp -./game/history.h -./game/player.cpp -./game/player.h -./game/pldrack.cpp -./game/pldrack.h -./game/rack.cpp -./game/rack.h -./game/results.cpp -./game/results.h -./game/round.cpp -./game/round.h -./game/training.cpp -./game/training.h -#./utils/eliottxt.cpp -./utils/game_io.h -./utils/game_io.cpp -./utils/ncurses.cpp -./utils/ncurses.h -./wxwin/auxframes.h -./wxwin/auxframes.cc -./wxwin/confdimdlg.h -./wxwin/confdimdlg.cc -./wxwin/configdb.h -./wxwin/configdb.cc -./wxwin/confsearch.h -./wxwin/confsearch.cc -./wxwin/ewx.h -./wxwin/gfxboard.h -./wxwin/gfxboard.cc -./wxwin/gfxresult.h -./wxwin/gfxresult.cc -./wxwin/main.cc -./wxwin/mainframe.h -./wxwin/mainframe.cc -./wxwin/printout.h -./wxwin/printout.cc -./wxwin/searchpanel.h -./wxwin/searchpanel.cc +dic/automaton.cpp +dic/automaton.h +dic/header.cpp +dic/header.h +dic/dic.cpp +dic/dic.h +dic/dic_internals.h +dic/dic_search.cpp +dic/hashtable.cpp +dic/hashtable.h +dic/regexp.cpp +dic/regexp.h +dic/tile.cpp +dic/tile.h +dic/compdic.cpp +dic/listdic.cpp +dic/regexpmain.cpp +game/bag.cpp +game/bag.h +game/board.cpp +game/board.h +game/board_cross.cpp +game/board_search.cpp +game/cross.cpp +game/cross.h +game/debug.h +game/duplicate.cpp +game/duplicate.h +game/freegame.cpp +game/freegame.h +game/game.cpp +game/game.h +game/game_io.cpp +game/history.cpp +game/history.h +game/player.cpp +game/player.h +game/pldrack.cpp +game/pldrack.h +game/rack.cpp +game/rack.h +game/results.cpp +game/results.h +game/round.cpp +game/round.h +game/training.cpp +game/training.h +# utils/eliottxt.cpp +utils/game_io.h +utils/game_io.cpp +utils/ncurses.cpp +utils/ncurses.h +wxwin/auxframes.h +wxwin/auxframes.cc +wxwin/confdimdlg.h +wxwin/confdimdlg.cc +wxwin/configdb.h +wxwin/configdb.cc +wxwin/confsearch.h +wxwin/confsearch.cc +wxwin/ewx.h +wxwin/gfxboard.h +wxwin/gfxboard.cc +wxwin/gfxresult.h +wxwin/gfxresult.cc +wxwin/main.cc +wxwin/mainframe.h +wxwin/mainframe.cc +wxwin/printout.h +wxwin/printout.cc +wxwin/searchpanel.h +wxwin/searchpanel.cc +qt/aux_window.cpp +qt/aux_window.h +qt/bag_widget.cpp +qt/bag_widget.h +qt/board_widget.cpp +qt/board_widget.h +qt/history_widget.cpp +qt/history_widget.h +qt/main_window.cpp +qt/main_window.h +qt/main.cpp +qt/new_game.cpp +qt/new_game.h +qt/player_widget.cpp +qt/player_widget.h +qt/score_widget.cpp +qt/score_widget.h +qt/ui/new_game.ui +qt/ui/player_widget.ui +qt/ui/main_window.ui diff --git a/qt/.cvsignore b/qt/.cvsignore new file mode 100644 index 0000000..b3ab4d5 --- /dev/null +++ b/qt/.cvsignore @@ -0,0 +1,7 @@ +.deps +Makefile +Makefile.in +qeliot +qeliot.exe +cscope.files +cscope.out diff --git a/qt/Makefile.am b/qt/Makefile.am new file mode 100644 index 0000000..498a27f --- /dev/null +++ b/qt/Makefile.am @@ -0,0 +1,80 @@ +# Eliot +# Copyright (C) 2008 Olivier Teulière +# Authors: Olivier Teulière +# +# 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 + +localedir = $(datadir)/locale + +if BUILD_QT + +AM_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\" @QT_CFLAGS@ -I$(top_srcdir) -I../intl -I$(top_srcdir)/dic -I$(top_srcdir)/game + +SUFFIXES=.ui.h .moc.cpp + +bin_PROGRAMS = qeliot + +EXTRA_DIST = \ + ui/main_window.ui \ + ui/new_game.ui \ + ui/player_widget.ui + +BUILT_SOURCES = \ + ui/main_window.ui.h \ + ui/new_game.ui.h \ + ui/player_widget.ui.h \ + new_game.moc.cpp \ + bag_widget.moc.cpp \ + score_widget.moc.cpp \ + board_widget.moc.cpp \ + player_widget.moc.cpp \ + history_widget.moc.cpp \ + aux_window.moc.cpp \ + main_window.moc.cpp + +qeliot_SOURCES = \ + qtcommon.h \ + bag_widget.cpp bag_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 \ + player_widget.cpp player_widget.h \ + aux_window.cpp aux_window.h \ + main_window.cpp main_window.h \ + main.cpp +qeliot_SOURCES += $(BUILT_SOURCES) + +qeliot_LDADD = @QT_LIBS@ ../game/libgame.a ../dic/libdic.a @LIBINTL@ + +# Generate the implementation file from the header +%.moc.cpp: %.h + $(MOC) -o $@ $< + +# Generate the header from the .ui file +# We want to translate the string with gettext, not Qt own way +%.ui.h: %.ui + mkdir -p ui + rm -f $@ $@.tmp + echo "// Note: Lines below generated by Eliot build system" > $@.tmp + echo "#include \"qtcommon.h\"" >> $@.tmp + echo "#define Q_(a,b) QString::fromUtf8(_(a))" >> $@.tmp + $(UIC) -tr "Q_" $< >> $@.tmp + sed 's/Q_(\"_(\\\"\(.*\)\\\")"/Q_("\1"/' $@.tmp >$@ + rm -f $@.tmp + + +endif + diff --git a/qt/aux_window.cpp b/qt/aux_window.cpp new file mode 100644 index 0000000..64513cf --- /dev/null +++ b/qt/aux_window.cpp @@ -0,0 +1,57 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 +#include +#include + +#include "aux_window.h" + + +AuxWindow::AuxWindow(QWidget &iWidget, QAction *iAction) + : m_widget(iWidget), m_action(iAction) +{ + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(&iWidget); + setLayout(layout); +} + + +AuxWindow::~AuxWindow() +{ + delete &m_widget; +} + + +void AuxWindow::showEvent(QShowEvent * event) +{ + if (m_action) + m_action->setChecked(true); + QWidget::showEvent(event); +} + + +void AuxWindow::hideEvent(QHideEvent * event) +{ + if (m_action) + m_action->setChecked(false); + QWidget::hideEvent(event); +} + diff --git a/qt/aux_window.h b/qt/aux_window.h new file mode 100644 index 0000000..ba4dbf4 --- /dev/null +++ b/qt/aux_window.h @@ -0,0 +1,58 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 AUX_WINDOW_H_ +#define AUX_WINDOW_H_ + +#include + +class QAction; + + +/** + * This class is a wrapper for widgets which should be in their own window. + * It ensures that all the auxiliary windows have a common behaviour, which + * we can change easily. + */ +class AuxWindow: public QWidget +{ + Q_OBJECT; + +public: + /** + * Constructor. The AuxWindow takes ownership of the given widget (i.e. it + * is responsible for its deletion). + * The optional action checked/unchecked when the window is shown/hidden + */ + AuxWindow(QWidget &iWidget, QAction *iAction = NULL); + ~AuxWindow(); + +protected: + virtual void showEvent(QShowEvent *event); + virtual void hideEvent(QHideEvent *event); + +private: + QWidget &m_widget; + + /// Action to check/uncheck + QAction *m_action; +}; + +#endif diff --git a/qt/bag_widget.cpp b/qt/bag_widget.cpp new file mode 100644 index 0000000..767b0ce --- /dev/null +++ b/qt/bag_widget.cpp @@ -0,0 +1,91 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 +#include +#include + +#include "bag_widget.h" +#include "qtcommon.h" +#include "dic.h" +#include "tile.h" +#include "bag.h" + +using namespace std; + + +BagWidget::BagWidget(QWidget *parent, const Bag *iBag) + : QTreeView(parent), m_bag(iBag) +{ + // Create the tree view + setEditTriggers(QAbstractItemView::NoEditTriggers); + setRootIsDecorated(false); + setSortingEnabled(true); + QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Expanding); + setSizePolicy(policy); + + // Associate the model to the view + m_model = new QStandardItemModel(this); + setModel(m_model); + updateModel(); +} + + +void BagWidget::setBag(const Bag *iBag) +{ + m_bag = iBag; + updateModel(); +} + + +void BagWidget::updateModel() +{ + m_model->clear(); + m_model->setColumnCount(2); + m_model->setHeaderData(0, Qt::Horizontal, _q("Letter"), Qt::DisplayRole); + m_model->setHeaderData(1, Qt::Horizontal, _q("Points"), Qt::DisplayRole); + + if (m_bag == NULL) + return; + + const vector& allTiles = m_bag->getDic().getAllTiles(); + vector::const_iterator it; + for (it = allTiles.begin(); it != allTiles.end(); ++it) + { + unsigned int nb = m_bag->in(*it); + if (nb != 0) + { + int rowNum = m_model->rowCount(); + m_model->insertRow(rowNum); + m_model->setData(m_model->index(rowNum, 0), + qfw(wstring(nb, it->toChar()))); + m_model->setData(m_model->index(rowNum, 1), it->getPoints()); + } + } + //resizeColumnToContents(0); + resizeColumnToContents(1); +} + + +QSize BagWidget::sizeHint() const +{ + return QSize(160, 300); +} + diff --git a/qt/bag_widget.h b/qt/bag_widget.h new file mode 100644 index 0000000..2467a64 --- /dev/null +++ b/qt/bag_widget.h @@ -0,0 +1,56 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 BAG_WIDGET_H_ +#define BAG_WIDGET_H_ + +#include + + +class Bag; +class QStandardItemModel; + +class BagWidget: public QTreeView +{ + Q_OBJECT; + +public: + explicit BagWidget(QWidget *parent = 0, const Bag *iBag = NULL); + +public slots: + void setBag(const Bag *iBag = NULL); + +protected: + /// Define a default size + virtual QSize sizeHint() const; + +private: + /// Encapsulated bag, can be NULL + const Bag *m_bag; + + /// Model of the bag + QStandardItemModel *m_model; + + /// Force synchronizing the model with the contents of the bag + void updateModel(); +}; + +#endif + diff --git a/qt/board_widget.cpp b/qt/board_widget.cpp new file mode 100644 index 0000000..ecd5a31 --- /dev/null +++ b/qt/board_widget.cpp @@ -0,0 +1,123 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 +#include +#include + +#include "board_widget.h" +#include "qtcommon.h" +#include "tile.h" +#include "board.h" + +using namespace std; + +const QColor BoardWidget::EmptyColour(Qt::white); +const QColor BoardWidget::L2Colour(34, 189, 240); +const QColor BoardWidget::L3Colour(29, 104, 240); +const QColor BoardWidget::W2Colour(255, 147, 196); +const QColor BoardWidget::W3Colour(240, 80, 94); +const QColor BoardWidget::TileColour(255, 235, 205); + + +BoardWidget::BoardWidget(QWidget *parent, const Board *iBoard) + : QFrame(parent), m_board(iBoard) +{ + setFrameStyle(QFrame::Panel); + // Use as much space as possible + QSizePolicy policy(QSizePolicy::Expanding, QSizePolicy::Expanding); + setSizePolicy(policy); + setMinimumSize(200, 200); +} + + +void BoardWidget::setBoard(const Board *iBoard) +{ + m_board = iBoard; + refresh(); +} + + +void BoardWidget::refresh() +{ + update(); +} + + +QSize BoardWidget::sizeHint() const +{ + return QSize(300, 300); +} + + +void BoardWidget::paintEvent(QPaintEvent *) +{ + int size = std::min(width(), height()); + int squareSize = (int)floor((size - 1) / (BOARD_MAX - BOARD_MIN + 2)); + + // XXX: Naive implementation: we repaint everything every time + QPainter painter(this); + //painter.setPen(Qt::NoPen); + for (unsigned int row = BOARD_MIN; row <= BOARD_MAX; ++row) + { + for (unsigned int col = BOARD_MIN; col <= BOARD_MAX; ++col) + { + // Set the brush color + if (m_board != NULL && !m_board->getTile(row, col).isEmpty()) + painter.setBrush(TileColour); + else if (Board::GetWordMultiplier(row, col) == 3) + painter.setBrush(W3Colour); + else if (Board::GetWordMultiplier(row, col) == 2) + painter.setBrush(W2Colour); + else if (Board::GetLetterMultiplier(row, col) == 3) + painter.setBrush(L3Colour); + else if (Board::GetLetterMultiplier(row, col) == 2) + painter.setBrush(L2Colour); + else + painter.setBrush(EmptyColour); + painter.drawRect((col - BOARD_MIN + 1) * squareSize, + (row - BOARD_MIN + 1) * squareSize, + squareSize, squareSize); + + // Draw the letter + if (m_board != NULL && !m_board->getTile(row, col).isEmpty()) + { + painter.drawText((col - BOARD_MIN + 1) * squareSize, + (row - BOARD_MIN + 1) * squareSize + 1, + squareSize, squareSize, + Qt::AlignCenter, + qfw(wstring(1, m_board->getTile(row, col).toChar()))); + } + } + } + // Draw the coordinates + for (unsigned x = 1; x <= BOARD_MAX - BOARD_MIN + 1; ++x) + { + painter.drawText(x * squareSize, 1, + squareSize, squareSize, + Qt::AlignCenter, + QString::number(x)); + painter.drawText(0, x * squareSize, + squareSize, squareSize, + Qt::AlignCenter, + QString(1, 'A' + x - 1)); + } +} + diff --git a/qt/board_widget.h b/qt/board_widget.h new file mode 100644 index 0000000..98f24c2 --- /dev/null +++ b/qt/board_widget.h @@ -0,0 +1,63 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 BOARD_WIDGET_H_ +#define BOARD_WIDGET_H_ + +#include + + +class Board; +class QTreeView; + +class BoardWidget: public QFrame +{ + Q_OBJECT; + +public: + explicit BoardWidget(QWidget *parent = 0, const Board *iBoard = NULL); + +public slots: + void setBoard(const Board *iBoard = NULL); + void refresh(); + +protected: + /// Define a default size + virtual QSize sizeHint() const; + /// Paint the board + virtual void paintEvent(QPaintEvent *iEvent); + +private: + /// Encapsulated board, can be NULL + const Board *m_board; + + /// Define a few background colours + //@{ + static const QColor EmptyColour; + static const QColor L2Colour; + static const QColor L3Colour; + static const QColor W2Colour; + static const QColor W3Colour; + static const QColor TileColour; + //@} +}; + +#endif + diff --git a/qt/history_widget.cpp b/qt/history_widget.cpp new file mode 100644 index 0000000..cd56d00 --- /dev/null +++ b/qt/history_widget.cpp @@ -0,0 +1,126 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 +#include +#include + +#include "history_widget.h" +#include "qtcommon.h" +#include "dic.h" +#include "tile.h" +#include "history.h" +#include "turn.h" +#include "move.h" + +using namespace std; + + +HistoryWidget::HistoryWidget(QWidget *parent, const History *iHistory) + : QTreeView(parent), m_history(iHistory) +{ + // Create the tree view + setEditTriggers(QAbstractItemView::NoEditTriggers); + setRootIsDecorated(false); + + // Associate the model to the view + m_model = new QStandardItemModel(this); + setModel(m_model); + updateModel(); +} + + +void HistoryWidget::setHistory(const History *iHistory) +{ + m_history = iHistory; + updateModel(); +} + + +void HistoryWidget::updateModel() +{ + m_model->clear(); + m_model->setColumnCount(6); + m_model->setHeaderData(0, Qt::Horizontal, _q("Turn"), Qt::DisplayRole); + m_model->setHeaderData(1, Qt::Horizontal, _q("Rack"), Qt::DisplayRole); + m_model->setHeaderData(2, Qt::Horizontal, _q("Word"), Qt::DisplayRole); + m_model->setHeaderData(3, Qt::Horizontal, _q("Ref"), Qt::DisplayRole); + m_model->setHeaderData(4, Qt::Horizontal, _q("Points"), Qt::DisplayRole); + m_model->setHeaderData(5, Qt::Horizontal, _q("Player"), Qt::DisplayRole); + + if (m_history != NULL) + { + for (unsigned int i = 0; i < m_history->getSize(); ++i) + { + int rowNum = m_model->rowCount(); + m_model->insertRow(rowNum); + + QColor color = Qt::black; + + const Turn& t = m_history->getTurn(i); + const Move& m = t.getMove(); + // Set data common to all moves) + m_model->setData(m_model->index(rowNum, 0), i + 1); + m_model->setData(m_model->index(rowNum, 1), + qfw(t.getPlayedRack().toString())); + m_model->setData(m_model->index(rowNum, 4), m.getScore()); + m_model->setData(m_model->index(rowNum, 5), t.getPlayer() + 1); + // Set the rest + if (m.getType() == Move::VALID_ROUND) + { + const Round &r = m.getRound(); + wstring coord = r.getCoord().toString(); + m_model->setData(m_model->index(rowNum, 2), qfw(r.getWord())); + m_model->setData(m_model->index(rowNum, 3), qfw(coord)); + color = Qt::black; + } + else if (m.getType() == Move::INVALID_WORD) + { + m_model->setData(m_model->index(rowNum, 2), + "<" + qfw(m.getBadWord()) + ">"); + m_model->setData(m_model->index(rowNum, 3), qfw(m.getBadCoord())); + color = Qt::red; + } + else if (m.getType() == Move::PASS) + { + m_model->setData(m_model->index(rowNum, 2), _q("(PASS)")); + color = Qt::blue; + } + else + { + m_model->setData(m_model->index(rowNum, 2), + "[-" + qfw(m.getChangedLetters()) + "]"); + color = Qt::blue; + } + // Set the color of the text + for (int col = 0; col < 6; ++col) + { + m_model->setData(m_model->index(rowNum, col), + QBrush(color), Qt::ForegroundRole); + } + } + } + + resizeColumnToContents(0); + resizeColumnToContents(3); + resizeColumnToContents(4); + resizeColumnToContents(5); +} + diff --git a/qt/history_widget.h b/qt/history_widget.h new file mode 100644 index 0000000..0ed68bd --- /dev/null +++ b/qt/history_widget.h @@ -0,0 +1,52 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 HISTORY_WIDGET_H_ +#define HISTORY_WIDGET_H_ + +#include + + +class History; +class QStandardItemModel; + +class HistoryWidget: public QTreeView +{ + Q_OBJECT; + +public: + explicit HistoryWidget(QWidget *parent = 0, const History *iHistory = NULL); + +public slots: + void setHistory(const History *iHistory = NULL); + +private: + /// Encapsulated history, can be NULL + const History *m_history; + + /// Model of the history + QStandardItemModel *m_model; + + /// Force synchronizing the model with the contents of the history + void updateModel(); +}; + +#endif + diff --git a/qt/main.cpp b/qt/main.cpp new file mode 100644 index 0000000..43d3f99 --- /dev/null +++ b/qt/main.cpp @@ -0,0 +1,44 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 "config.h" + +#include +#include "main_window.h" + +int main(int argc, char **argv) +{ +#if HAVE_SETLOCALE + // Set locale via LC_ALL + setlocale(LC_ALL, ""); +#endif + +#if ENABLE_NLS + // Set the message domain + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); +#endif + + QApplication app(argc, argv); + MainWindow qmain; + qmain.move(200, 200); + qmain.show(); + return app.exec(); +} diff --git a/qt/main_window.cpp b/qt/main_window.cpp new file mode 100644 index 0000000..c2ae125 --- /dev/null +++ b/qt/main_window.cpp @@ -0,0 +1,265 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 "config.h" + +#include +#include +#include + +#include "main_window.h" +#include "dic.h" +#include "encoding.h" +#include "header.h" +#include "game.h" +#include "freegame.h" +#include "player.h" +#include "debug.h" +#include "new_game.h" +#include "bag_widget.h" +#include "board_widget.h" +#include "score_widget.h" +#include "player_widget.h" +#include "history_widget.h" +#include "aux_window.h" +#include "qtcommon.h" + +#include "round.h" +#include "coord.h" + + +MainWindow::MainWindow(QWidget *iParent) + : QMainWindow(iParent), m_dic(NULL), m_game(NULL), m_newGame(NULL), + m_bagWindow(NULL) +{ + m_ui.setupUi(this); + + // Cascading signals + QObject::connect(this, SIGNAL(gameChanged(const Game*)), + this, SLOT(gameUpdated())); + + // Create the main window + BoardWidget *boardWidget = new BoardWidget; + QObject::connect(this, SIGNAL(boardChanged(const Board*)), + boardWidget, SLOT(setBoard(const Board*))); + + QDockWidget *dock = new QDockWidget; + dock->setWidget(boardWidget); + boardWidget->setWindowTitle(_q("Board")); + + ScoreWidget *scores = new ScoreWidget; + QObject::connect(this, SIGNAL(gameChanged(const Game*)), + scores, SLOT(setGame(const Game*))); + + HistoryWidget *historyWidget = new HistoryWidget; + QObject::connect(this, SIGNAL(historyChanged(const History*)), + historyWidget, SLOT(setHistory(const History*))); + + QHBoxLayout *hlayout = new QHBoxLayout; + hlayout->addWidget(dock); + hlayout->addWidget(scores); + hlayout->addWidget(historyWidget); + + m_ui.groupBoxTest->setLayout(hlayout); + + // XXX: temporary, for testing purposes! + try + { + m_dic = new Dictionary("/home/ipkiss/ods5.dawg"); + } + catch (...) + { + // Ignore the error silently :) + } +} + + +MainWindow::~MainWindow() +{ + delete m_bagWindow; + delete m_game; + delete m_dic; +} + + +void MainWindow::on_action_About_triggered() +{ + QString msg; + msg.sprintf("Eliot %s\n\n", VERSION); + msg += _q( \ + "Copyright (C) 1999-2008 - Antoine Fraboulet & Olivier Teuliere\n\n" \ + "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."); + // QMessageBox::about() doesn't add the nice information icon, so we create + // the box manually (not much work...) + QMessageBox *aboutBox = new QMessageBox(QMessageBox::Information, + _q("About Eliot"), msg, QMessageBox::Ok, this); + aboutBox->exec(); +} + + +void MainWindow::on_action_Bag_triggered() +{ + if (m_bagWindow == NULL) + { + // Create the bag window + BagWidget *bagWidget = + new BagWidget(NULL, m_game ? &m_game->getBag() : NULL); + m_bagWindow = new AuxWindow(*bagWidget, m_ui.action_Bag); + QObject::connect(this, SIGNAL(bagChanged(const Bag*)), + bagWidget, SLOT(setBag(const Bag*))); + // XXX + m_bagWindow->move(20, 20); + } + if (m_bagWindow->isVisible()) + m_bagWindow->hide(); + else + m_bagWindow->show(); +} + + +void MainWindow::on_action_ChooseDic_triggered() +{ + QString fileName = + QFileDialog::getOpenFileName(this, _q("Choose a dictionary"), "", "*.dawg"); + if (!fileName.isEmpty()) + { + try + { + Dictionary *dic = new Dictionary(qtl(fileName)); + delete m_dic; + m_dic = dic; + emit dicChanged(fileName, qfw(m_dic->getHeader().getName())); + } + catch (std::exception &e) + { + displayErrorMsg(e.what()); + } + } +} + + +void MainWindow::on_action_New_Game_triggered() +{ + if (m_dic == NULL) + { + displayErrorMsg(_q("You have to select a dictionary first!")); + return; + } + + if (m_newGame == NULL) + m_newGame = new NewGame(this); + + int res = m_newGame->exec(); + if (res == QDialog::Rejected) + return; + + m_game = m_newGame->createGame(*m_dic); + if (m_game == NULL) + return; + + int nbTabs = m_ui.tabWidgetPlayers->count(); + for (int i = 0; i < nbTabs; ++i) + m_ui.tabWidgetPlayers->removeTab(0); + + m_game->start(); + for (unsigned int i = 0; i < m_game->getNPlayers(); ++i) + { + PlayerWidget *r = new PlayerWidget(NULL, i, m_game); + QObject::connect(this, SIGNAL(rackChanged()), r, SLOT(refresh())); + QObject::connect(r, SIGNAL(playingWord(unsigned int, QString, QString)), + this, SLOT(playerPlays(unsigned int, QString, QString))); + QObject::connect(r, SIGNAL(passing(unsigned int, QString)), + this, SLOT(playerPasses(unsigned int, QString))); + m_ui.tabWidgetPlayers->addTab(r, qfw(m_game->getPlayer(i).getName())); + } + + emit gameChanged(m_game); +} + + +void MainWindow::gameUpdated() +{ + emit bagChanged(&m_game->getBag()); + emit boardChanged(&m_game->getBoard()); + emit historyChanged(&m_game->getHistory()); + emit rackChanged(); +} + + +void MainWindow::playerPlays(unsigned int p, QString iWord, QString iCoord) +{ + cerr << "Player " << p << " plays \"" << convertToMb(qtw(iWord)) + << "\" in " << convertToMb(qtw(iCoord)) << endl; + int res = m_game->play(qtw(iCoord), qtw(iWord)); + if (res) + { + // XXX: tmp + QString error; + error.sprintf("Error: %d", res); + displayErrorMsg(error, qfl("playing word")); + return; + } + emit gameChanged(m_game); +} + + +void MainWindow::playerPasses(unsigned int p, QString iChangedLetters) +{ + if (iChangedLetters == "") + { + cerr << "Player " << p << " passes" << endl; + } + else + { + cerr << "Player " << p << " changes \"" + << convertToMb(qtw(iChangedLetters)) << "\"" << endl; + } + + FreeGame *free = dynamic_cast(m_game); + ASSERT(free != NULL, + "MainWindow::playerPasses() called while not in a free game"); + + int res = free->pass(qtw(iChangedLetters)); + if (res) + { + // XXX: tmp + QString error; + error.sprintf("Error: %d", res); + displayErrorMsg(error, qfl("playing word")); + return; + } + emit bagChanged(&m_game->getBag()); + emit boardChanged(&m_game->getBoard()); + emit historyChanged(&m_game->getHistory()); + emit rackChanged(); +} + + +void MainWindow::displayErrorMsg(QString iMsg, QString iContext) +{ + if (iContext == "") + iContext = PACKAGE_NAME; + + QMessageBox::warning(this, iContext, iMsg); +} + diff --git a/qt/main_window.h b/qt/main_window.h new file mode 100644 index 0000000..08eb7b1 --- /dev/null +++ b/qt/main_window.h @@ -0,0 +1,87 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 MAIN_WINDOW_H_ +#define MAIN_WINDOW_H_ + +#include + +#include + + +class Dictionary; +class Bag; +class Board; +class History; +class Game; +class NewGame; +class AuxWindow; + +class MainWindow: public QMainWindow +{ + Q_OBJECT; + +public: + MainWindow(QWidget *iParent = 0); + ~MainWindow(); + +signals: + void dicChanged(QString iDicFile, QString iDicName); + void gameChanged(const Game *iGame); + void bagChanged(const Bag *iBag); + void boardChanged(const Board *iBoard); + void historyChanged(const History *iHistory); + void rackChanged(); + +public slots: + void playerPlays(unsigned int p, QString iWord, QString iCoord); + void playerPasses(unsigned int p, QString iLetters); + +private slots: + /// Emit various specific signals + void gameUpdated(); + + void on_action_About_triggered(); + void on_action_Bag_triggered(); + void on_action_ChooseDic_triggered(); + void on_action_New_Game_triggered(); + +private: + /// Display an error message to the user + void displayErrorMsg(QString iMsg, QString iContext = QString()); + + /// Current dictionary + const Dictionary *m_dic; + + /// Current game + Game *m_game; + + /// The UI file generated with Qt Designer + Ui::MainWindow m_ui; + + /// Dialog for creating a new game + NewGame *m_newGame; + + /// Bag window + AuxWindow *m_bagWindow; +}; + +#endif + diff --git a/qt/new_game.cpp b/qt/new_game.cpp new file mode 100644 index 0000000..ba87f59 --- /dev/null +++ b/qt/new_game.cpp @@ -0,0 +1,242 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 +#include +#include + +#include "new_game.h" +#include "qtcommon.h" +#include "game_factory.h" +#include "game.h" +#include "training.h" +#include "freegame.h" +#include "duplicate.h" +#include "player.h" + + +const QString NewGame::kHUMAN = _q("Human"); +const QString NewGame::kAI = _q("AI"); + + +NewGame::NewGame(QWidget *iParent) + : QDialog(iParent) +{ + setupUi(this); + + // Initialize the model of the default players + m_model = new QStandardItemModel(2, 2, this); + m_model->setHeaderData(0, Qt::Horizontal, _q("Name"), Qt::DisplayRole); + m_model->setHeaderData(1, Qt::Horizontal, _q("Type"), Qt::DisplayRole); + m_model->setData(m_model->index(0, 0), _q("HumanPlayer")); + m_model->setData(m_model->index(0, 1), kHUMAN); + m_model->setData(m_model->index(1, 0), _q("Eliot")); + m_model->setData(m_model->index(1, 1), kAI); + + // Initialize the QTreeView with the model we just created + treeViewPlayers->setModel(m_model); + PlayersDelegate *delegate = new PlayersDelegate(this); + treeViewPlayers->setItemDelegateForColumn(1, delegate); + + // Enable the Remove button only when there is a selection in the tree + QObject::connect(treeViewPlayers->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, + SLOT(enableRemoveButton(const QItemSelection&, const QItemSelection&))); + // Enable the Ok button only when if there are enough players for the + // current mode + QObject::connect(m_model, SIGNAL(rowsInserted(const QModelIndex&, int, int)), + this, SLOT(enableOkButton())); + QObject::connect(m_model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), + this, SLOT(enableOkButton())); + QObject::connect(comboBoxMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(enableOkButton())); + + // Install a custom event filter, to remove the selection when the + // "Delete" key is pressed + PlayersEventFilter *filter = new PlayersEventFilter(this); + treeViewPlayers->installEventFilter(filter); + QObject::connect(filter, SIGNAL(deletePressed()), + this, SLOT(on_pushButtonRemove_clicked())); +} + + +Game * NewGame::createGame(const Dictionary &iDic) const +{ + // Create the game + Game *game = NULL; + if (comboBoxMode->currentText() == _q("Training")) + game = GameFactory::Instance()->createTraining(iDic); + else if (comboBoxMode->currentText() == _q("Free game")) + game = GameFactory::Instance()->createFreeGame(iDic); + else + game = GameFactory::Instance()->createDuplicate(iDic); + + // Add the players + if (comboBoxMode->currentText() != _q("Training")) + { + for (int num = 0; num < m_model->rowCount(); ++num) + { + QString name = m_model->data(m_model->index(num, 0)).toString(); + if (name == "") + name = _q("Player %1").arg(num + 1); + QString type = m_model->data(m_model->index(num, 1)).toString(); + if (type == kHUMAN) + game->addHumanPlayer(); + else + game->addAIPlayer(); + // FIXME: ugly const_cast, because of the awkward Game API + // (we should have addPlayer(Player &) instead) + const Player *player = &game->getPlayer(game->getNPlayers() - 1); + const_cast(player)->setName(qtw(name)); + } + } + else + { + const_cast(&game->getPlayer(0))->setName(qtw(_q("Training"))); + } + + // Joker game? + if (checkBoxJoker->isChecked()) + game->setVariant(Game::kJOKER); + + return game; +} + + +void NewGame::enableOkButton() +{ + // Enable the "Ok" button: + // - always in training mode + // - if there is at least one player in duplicate mode + // - if there are at least 2 players in free game mode + bool disable = + (comboBoxMode->currentText() == _q("Duplicate") && m_model->rowCount() < 1) || + (comboBoxMode->currentText() == _q("Free game") && m_model->rowCount() < 2); + buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!disable); +} + + +void NewGame::enableRemoveButton(const QItemSelection &iSelected, + const QItemSelection &) +{ + // Enable the "Remove" button iff at least one line in the tree view + // is selected + pushButtonRemove->setEnabled(!iSelected.indexes().empty()); +} + + +void NewGame::on_comboBoxMode_activated(int iIndex) +{ + groupBoxPlayers->setEnabled(iIndex != 2); +} + + +void NewGame::on_pushButtonAdd_clicked() +{ + // Add a new row + int rowNum = m_model->rowCount(); + bool res = m_model->insertRow(rowNum); + if (!res) + return; + + // Change the contents of the row + m_model->setData(m_model->index(rowNum, 0), lineEditName->displayText()); + m_model->setData(m_model->index(rowNum, 1), comboBoxType->currentText()); +} + + +void NewGame::on_pushButtonRemove_clicked() +{ + QModelIndexList indexList = treeViewPlayers->selectionModel()->selectedIndexes(); + if (indexList.empty()) + return; + m_model->removeRow(indexList.front().row()); +} + + + +PlayersDelegate::PlayersDelegate(QObject *parent) + : QItemDelegate(parent) +{ +} + + +QWidget *PlayersDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &, + const QModelIndex &) const +{ + QComboBox *editor = new QComboBox(parent); + editor->addItem(NewGame::kHUMAN); + editor->addItem(NewGame::kAI); + return editor; +} + + +void PlayersDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + QComboBox *combo = static_cast(editor); + QString text = index.model()->data(index, Qt::DisplayRole).toString(); + combo->setCurrentIndex(combo->findText(text)); +} + + +void PlayersDelegate::setModelData(QWidget *editor, + QAbstractItemModel *model, + const QModelIndex &index) const +{ + QComboBox *combo = static_cast(editor); + model->setData(index, combo->currentText()); +} + + +void PlayersDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, + const QModelIndex &) const +{ + editor->setGeometry(option.rect); +} + + + +PlayersEventFilter::PlayersEventFilter(QObject *parent) + : QObject(parent) +{ +} + + +bool PlayersEventFilter::eventFilter(QObject *obj, QEvent *event) +{ + // If the Delete key is pressed, remove the selected line, if any + if (event->type() == QEvent::KeyPress) + { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Delete) + { + emit deletePressed(); + return true; + } + } + + // Standard event processing + return QObject::eventFilter(obj, event); +} + diff --git a/qt/new_game.h b/qt/new_game.h new file mode 100644 index 0000000..2487067 --- /dev/null +++ b/qt/new_game.h @@ -0,0 +1,109 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 NEW_GAME_H_ +#define NEW_GAME_H_ + +#include +#include + +#include + + +class Dictionary; +class Game; +class QStandardItemModel; + +class NewGame: public QDialog, private Ui::NewGameDialog +{ + Q_OBJECT; + +public: + explicit NewGame(QWidget *iParent = 0); + + /// Possible values for the player type + static const QString kHUMAN; + static const QString kAI; + + /** + * Create and return a game object from the information of the dialog. + * The Game object is always valid + */ + Game * createGame(const Dictionary& iDic) const; + +private slots: + void enableOkButton(); + void enableRemoveButton(const QItemSelection&, const QItemSelection&); + + // The following slots are automatically connected + void on_comboBoxMode_activated(int); + void on_pushButtonAdd_clicked(); + void on_pushButtonRemove_clicked(); + +private: + /// Model of the players + QStandardItemModel *m_model; +}; + + +/// Delegate used for the edition of the players display +class PlayersDelegate: public QItemDelegate +{ + Q_OBJECT; + +public: + explicit PlayersDelegate(QObject *parent = 0); + virtual ~PlayersDelegate() {} + + // Implement the needed methods + virtual QWidget *createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const; + virtual void setEditorData(QWidget *editor, + const QModelIndex &index) const; + virtual void setModelData(QWidget *editor, + QAbstractItemModel *model, + const QModelIndex &index) const; + + virtual void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + + +/// Event filter used for the edition of the players display +class PlayersEventFilter: public QObject +{ + Q_OBJECT; + +public: + explicit PlayersEventFilter(QObject *parent = 0); + virtual ~PlayersEventFilter() {} + +signals: + /// As its name indicates... + void deletePressed(); + +protected: + virtual bool eventFilter(QObject *obj, QEvent *event); +}; + +#endif + diff --git a/qt/player_widget.cpp b/qt/player_widget.cpp new file mode 100644 index 0000000..def0101 --- /dev/null +++ b/qt/player_widget.cpp @@ -0,0 +1,212 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 +#include +#include +#include + +#include "player_widget.h" +#include "qtcommon.h" +#include "game.h" +#include "player.h" +#include "pldrack.h" +#include "coord.h" +#include "dic.h" +#include "debug.h" + +#include "encoding.h" + + +/// Validator used for the "change letters" line edit +class ChangeValidator: public QValidator +{ +public: + explicit ChangeValidator(QObject *parent, + const QLineEdit &iLineEdit); + virtual State validate(QString &input, int &pos) const; + +private: + const QLineEdit &m_lineEdit; +}; + + +/// Validator used for the "change letters" 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; +}; + + +PlayerWidget::PlayerWidget(QWidget *parent, unsigned int iPlayerNb, + const Game *iGame) + : QWidget(parent), m_game(iGame), m_player(iPlayerNb) +{ + setupUi(this); + + if (m_game) + { + lineEditPlay->setValidator(new PlayWordValidator(this, m_game->getDic())); + + // Do not allow messing with AI players + if (!m_game->getPlayer(m_player).isHuman()) + setEnabled(false); + + // Changing the rack is authorized only in training mode + lineEditRack->setReadOnly(m_game->getMode() != Game::kTRAINING); + } + if (m_game == NULL || m_game->getMode() != Game::kFREEGAME) + { + // Hide the freegame-specific controls + labelChange->hide(); + lineEditChange->hide(); + pushButtonChange->hide(); + pushButtonPass->hide(); + } + else + { + lineEditChange->setValidator(new ChangeValidator(this, *lineEditRack)); + } + + refresh(); +} + + +QSize PlayerWidget::sizeHint() const +{ + return QSize(100, 100); +} + + +void PlayerWidget::refresh() +{ + lineEditPlay->clear(); + + if (m_game == NULL) + { + // XXX + lineEditRack->clear(); + return; + } + + const PlayedRack &pld = m_game->getPlayer(m_player).getCurrentRack(); + lineEditRack->setText(qfw(pld.toString(PlayedRack::RACK_EXTRA))); + lineEditPlay->clear(); + lineEditChange->clear(); +} + + +void PlayerWidget::on_pushButtonShuffle_clicked() +{ + // TODO +} + + +void PlayerWidget::on_lineEditPlay_textChanged() +{ + pushButtonPlay->setEnabled(lineEditPlay->hasAcceptableInput()); +} + + +void PlayerWidget::on_lineEditChange_textChanged() +{ + pushButtonChange->setEnabled(lineEditChange->hasAcceptableInput() && + lineEditChange->text() != ""); + pushButtonPass->setEnabled(lineEditChange->text() == ""); +} + + +void PlayerWidget::on_lineEditPlay_returnPressed() +{ + QStringList items = lineEditPlay->text().split(' ', QString::SkipEmptyParts); + ASSERT(items.size() == 2, "Bug found in the validator"); + emit playingWord(m_player, items[0], items[1]); +} + + +void PlayerWidget::on_lineEditChange_returnPressed() +{ + emit passing(m_player, lineEditChange->text()); +} + + + +ChangeValidator::ChangeValidator(QObject *parent, + const QLineEdit &iLineEdit) + : QValidator(parent), m_lineEdit(iLineEdit) +{ +} + + +QValidator::State ChangeValidator::validate(QString &input, int &) const +{ + QString rack = m_lineEdit.text(); + if (input.size() > rack.size()) + return Intermediate; + // The letters to change must be in the rack + for (int i = 0; i < input.size(); ++i) + { + if (input.count(input[i], Qt::CaseInsensitive) > + rack.count(input[i], Qt::CaseInsensitive)) + { + return Intermediate; + } + } + return Acceptable; +} + + + +PlayWordValidator::PlayWordValidator(QObject *parent, + const Dictionary &iDic) + : QValidator(parent), m_dic(iDic) +{ +} + + +QValidator::State PlayWordValidator::validate(QString &input, int &) const +{ + if (input == "") + return Intermediate; + + QStringList items = input.split(' ', QString::SkipEmptyParts); + + // The string is invalid if it contains characters not present + // in the dictionary + if (!m_dic.validateLetters(qtw(items[0]))) + return Invalid; + + if (items.size() != 2 || items[0].size() < 2) + return Intermediate; + + // Check coordinates + Coord c(qtw(items[1])); + if (!c.isValid()) + return Intermediate; + + return Acceptable; +} + diff --git a/qt/player_widget.h b/qt/player_widget.h new file mode 100644 index 0000000..a523244 --- /dev/null +++ b/qt/player_widget.h @@ -0,0 +1,68 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 RACK_WIDGET_H_ +#define RACK_WIDGET_H_ + +#include +#include "ui/player_widget.ui.h" + + +class QLineEdit; +class Game; + +class PlayerWidget: public QWidget, private Ui::PlayerWidget +{ + Q_OBJECT; + +public: + explicit PlayerWidget(QWidget *parent = 0, + unsigned int iPlayerNb = 0, + const Game *iGame = NULL); + QSize sizeHint() const; + +signals: + void playingWord(unsigned int iPlayer, QString iWord, QString iCoord); + void passing(unsigned int iPlayer, QString iChangedLetters); + +public slots: + void refresh(); + +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_lineEditChange_textChanged(); + void on_lineEditPlay_returnPressed(); + void on_lineEditChange_returnPressed(); + +private: + /// Encapsulated game, can be NULL + const Game *m_game; + + /// Encapsulated player, valid iff m_game is not NULL + unsigned int m_player; + +}; + +#endif + diff --git a/qt/qtcommon.h b/qt/qtcommon.h new file mode 100644 index 0000000..7775635 --- /dev/null +++ b/qt/qtcommon.h @@ -0,0 +1,46 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 QT_COMMON_H_ +#define QT_COMMON_H_ + +#if ENABLE_NLS +# include +# define _(String) gettext(String) +// Apparently needed on Windows, where libintl.h defines sprintf +// as libintl_sprintf... +# undef sprintf +#else +# define _(String) String +#endif + +// Convert to/from utf-8 char* +#define qfu(s) QString::fromUtf8(s) +#define qtu(s) (s).toUtf8().data() +// Convert to/from std::wstring +#define qfw(s) QString::fromStdWString(s) +#define qtw(s) (s).toStdWString().data() +// Convert to/from local encoding +#define qfl(s) QString::fromLocal8Bit(s) +#define qtl(s) (s).toLocal8Bit().data() +// Translation macro to use gettext +#define _q(s) qfu(_(s)) + +#endif diff --git a/qt/score_widget.cpp b/qt/score_widget.cpp new file mode 100644 index 0000000..665379b --- /dev/null +++ b/qt/score_widget.cpp @@ -0,0 +1,83 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 +#include + +#include "score_widget.h" +#include "qtcommon.h" +#include "game.h" +#include "player.h" + +using namespace std; + + +ScoreWidget::ScoreWidget(QWidget *parent, const Game *iGame) + : QTreeView(parent), m_game(iGame) +{ + // Create the tree view + setEditTriggers(QAbstractItemView::NoEditTriggers); + setRootIsDecorated(false); + setSortingEnabled(true); + QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Expanding); + setSizePolicy(policy); + + // Associate the model to the view + m_model = new QStandardItemModel(this); + setModel(m_model); + updateModel(); +} + + +void ScoreWidget::setGame(const Game *iGame) +{ + m_game = iGame; + updateModel(); +} + + +void ScoreWidget::updateModel() +{ + m_model->clear(); + m_model->setColumnCount(2); + m_model->setHeaderData(0, Qt::Horizontal, _q("Player"), Qt::DisplayRole); + m_model->setHeaderData(1, Qt::Horizontal, _q("Score"), Qt::DisplayRole); + resizeColumnToContents(0); + //resizeColumnToContents(1); + + if (m_game == NULL) + return; + + for (unsigned int i = 0; i < m_game->getNPlayers(); ++i) + { + const Player &p = m_game->getPlayer(i); + int rowNum = m_model->rowCount(); + m_model->insertRow(rowNum); + m_model->setData(m_model->index(rowNum, 0), qfw(p.getName())); + m_model->setData(m_model->index(rowNum, 1), p.getPoints()); + } +} + + +QSize ScoreWidget::sizeHint() const +{ + return QSize(160, 100); +} + diff --git a/qt/score_widget.h b/qt/score_widget.h new file mode 100644 index 0000000..94c6733 --- /dev/null +++ b/qt/score_widget.h @@ -0,0 +1,56 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2008 Olivier Teulière + * Authors: Olivier Teulière + * + * 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 SCORE_WIDGET_H_ +#define SCORE_WIDGET_H_ + +#include + + +class Game; +class QStandardItemModel; + +class ScoreWidget: public QTreeView +{ + Q_OBJECT; + +public: + explicit ScoreWidget(QWidget *parent = 0, const Game *iGame = NULL); + +public slots: + void setGame(const Game *iGame = NULL); + +protected: + /// Define a default size + virtual QSize sizeHint() const; + +private: + /// Encapsulated game, can be NULL + const Game *m_game; + + /// Model of the game + QStandardItemModel *m_model; + + /// Force synchronizing the model with the contents of the game + void updateModel(); +}; + +#endif + diff --git a/qt/ui/main_window.ui b/qt/ui/main_window.ui new file mode 100644 index 0000000..350d717 --- /dev/null +++ b/qt/ui/main_window.ui @@ -0,0 +1,146 @@ + + MainWindow + + + + 0 + 0 + 683 + 589 + + + + Eliot + + + ../../../bzr-cppdic/wxwin/eliot.xpm + + + + + + + + 0 + 0 + + + + GroupBox + + + + + + + + 0 + 0 + + + + + _("No game") + + + + + + + + + + 0 + 0 + 683 + 28 + + + + + _("&Game") + + + + + + + + &Settings + + + + + + _("&Help") + + + + + + + _("&Windows") + + + + + + + + + + + + &Choose dictionary... + + + + + _("&About...") + + + About Eliot + + + + + _("&Quit") + + + + + _("&New game...") + + + Ctrl+N + + + + + true + + + _("&Bag") + + + + + + + action_Quit + triggered() + MainWindow + close() + + + -1 + -1 + + + 260 + 215 + + + + + diff --git a/qt/ui/new_game.ui b/qt/ui/new_game.ui new file mode 100644 index 0000000..b875d40 --- /dev/null +++ b/qt/ui/new_game.ui @@ -0,0 +1,213 @@ + + NewGameDialog + + + + 0 + 0 + 374 + 327 + + + + _("New game") + + + true + + + + + + _("Game type") + + + + + + _("Mode:") + + + + + + + + _("Duplicate") + + + + + _("Free game") + + + + + _("Training") + + + + + + + + Qt::Horizontal + + + + 16 + 26 + + + + + + + + _("Joker game") + + + + + + + + + + _("Players") + + + + + + QAbstractItemView::AnyKeyPressed|QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::NoEditTriggers + + + false + + + + + + + + + _("Name:") + + + + + + + _("Player") + + + + + + + _("Type:") + + + + + + + + _("Human") + + + + + _("AI") + + + + + + + + + + + + _("Add player") + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + _("Remove player") + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + NewGameDialog + accept() + + + 194 + 309 + + + 173 + 274 + + + + + buttonBox + rejected() + NewGameDialog + reject() + + + 101 + 298 + + + 75 + 275 + + + + + diff --git a/qt/ui/player_widget.ui b/qt/ui/player_widget.ui new file mode 100644 index 0000000..21d7b23 --- /dev/null +++ b/qt/ui/player_widget.ui @@ -0,0 +1,116 @@ + + PlayerWidget + + + + 0 + 0 + 599 + 170 + + + + + + + _("Rack:") + + + + + + + true + + + + + + + false + + + _("Shuffle") + + + + + + + _("Play a word:") + + + + + + + _("To play a word, enter the word (case sensitive) and its coordinates. E.g.: WORD H4") + + + dummy + + + + + + + _("Play") + + + + + + + _("Change letters:") + + + + + + + _("Enter the letters you want to change") + + + dummy + + + + + + + _("Change") + + + + + + + _("Pass") + + + + + + + _("To play a word, enter the word (case sensitive) and its coordinates. E.g.: WORD H4") + + + + + + + Qt::Vertical + + + + 20 + 20 + + + + + + + + +