Qt interface:

- Ensure unicity of players names (in the interface only, not the core)
 - Update the main window title with the game type
 - Display the player name in the history
 - The history widget is now able not to align the rack and the corresponding word
 - The dictionary path is remembered in the preferences
 - Ask for confirmation when loading a dictionary in the middle of a game
 - Preview tiles are coloured differently, and joker characters are in lowercase
This commit is contained in:
Olivier Teulière 2008-01-26 16:58:46 +00:00
parent d8780822c2
commit 457b368ae7
11 changed files with 242 additions and 39 deletions

View file

@ -37,6 +37,7 @@ 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);
const QColor BoardWidget::PreviewColour(183, 183, 123);
BoardWidget::BoardWidget(QWidget *parent)
@ -83,7 +84,12 @@ void BoardWidget::paintEvent(QPaintEvent *)
{
// Set the brush color
if (m_game != NULL && !m_game->getBoard().getTile(row, col).isEmpty())
painter.setBrush(TileColour);
{
if (m_game->getBoard().getCharAttr(row, col) & ATTR_TEST)
painter.setBrush(PreviewColour);
else
painter.setBrush(TileColour);
}
else if (Board::GetWordMultiplier(row, col) == 3)
painter.setBrush(W3Colour);
else if (Board::GetWordMultiplier(row, col) == 2)
@ -101,11 +107,14 @@ void BoardWidget::paintEvent(QPaintEvent *)
// Draw the letter
if (m_game != NULL && !m_game->getBoard().getTile(row, col).isEmpty())
{
wchar_t chr = m_game->getBoard().getTile(row, col).toChar();
if (m_game->getBoard().getCharAttr(row, col) & ATTR_JOKER)
chr = towlower(chr);
painter.drawText((col - BOARD_MIN + 1) * squareSize,
(row - BOARD_MIN + 1) * squareSize + 1,
squareSize, squareSize,
Qt::AlignCenter,
qfw(wstring(1, m_game->getBoard().getTile(row, col).toChar())));
qfw(wstring(1, chr)));
}
}
}

View file

@ -56,6 +56,7 @@ private:
static const QColor W2Colour;
static const QColor W3Colour;
static const QColor TileColour;
static const QColor PreviewColour;
//@}
};

View file

@ -22,8 +22,10 @@
#include <QtGui/QTreeView>
#include <QtGui/QTabWidget>
#include <QtGui/QStandardItemModel>
#include <QtCore/QSettings>
#include "history_widget.h"
#include "prefs_dialog.h"
#include "qtcommon.h"
#include "game.h"
#include "player.h"
@ -55,9 +57,11 @@ HistoryWidget::HistoryWidget(QWidget *parent)
void HistoryWidget::setHistory(const History *iHistory,
const Game *iGame,
bool iIsForPlayer)
{
m_history = iHistory;
m_game = iGame;
m_forPlayer = iIsForPlayer;
updateModel();
}
@ -82,24 +86,41 @@ void HistoryWidget::updateModel()
m_model->setHeaderData(5, Qt::Horizontal, _q("Player"), Qt::DisplayRole);
}
if (m_history != NULL)
if (m_history != NULL && m_history->getSize() != 0)
{
// Should we align the rack with its solution?
QSettings qs;
bool align = qs.value(PrefsDialog::kINTF_ALIGN_HISTORY).toBool();
if (!align)
m_model->insertRow(0);
for (unsigned int i = 0; i < m_history->getSize(); ++i)
{
int rowNum = m_model->rowCount();
m_model->insertRow(rowNum);
int prevRowNum;
if (align)
prevRowNum = rowNum;
else
prevRowNum = rowNum - 1;
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),
// Set data common to all moves
m_model->setData(m_model->index(prevRowNum, 0), i + 1);
m_model->setData(m_model->index(prevRowNum, 1),
qfw(t.getPlayedRack().toString()));
m_model->setData(m_model->index(rowNum, 4), m.getScore());
if (!m_forPlayer)
m_model->setData(m_model->index(rowNum, 5), t.getPlayer() + 1);
if (!m_forPlayer && m_game != NULL)
{
const wstring &name = m_game->getPlayer(t.getPlayer()).getName();
m_model->setData(m_model->index(rowNum, 5), qfw(name));
}
// Set the rest
if (m.getType() == Move::VALID_ROUND)
{
@ -127,6 +148,7 @@ void HistoryWidget::updateModel()
"[-" + qfw(m.getChangedLetters()) + "]");
color = Qt::blue;
}
// Set the color of the text
for (int col = 0; col < 6; ++col)
{
@ -159,33 +181,40 @@ void HistoryTabWidget::setGame(const Game *iGame)
{
m_game = iGame;
// Keep only the Game tab, because it is nicer to have something, even
// if it is empty
int nbTabs = count();
for (int i = nbTabs - 1; i > 0; --i)
{
setCurrentIndex(i);
// Cut all the connections with the page (needed because removeTab()
// doesn't really destroy the widget)
disconnect(currentWidget());
removeTab(i);
}
if (m_game == NULL)
{
// Cut all the connections with the pages
disconnect();
// Keep only the Game tab, because it is nicer to have something, even
// if it is empty
int nbTabs = count();
for (int i = nbTabs - 1; i > 0; --i)
removeTab(i);
// Tell the remaining tab that there is no more history to display
m_gameHistoryWidget->setHistory(NULL);
}
else
{
// Refresh the Game tab
m_gameHistoryWidget->setHistory(&m_game->getHistory());
m_gameHistoryWidget->setHistory(&m_game->getHistory(), m_game);
QObject::connect(this, SIGNAL(refreshSignal()),
m_gameHistoryWidget, SLOT(refresh()));
// In training mode, the players history is completely useless
if (m_game->getMode() == Game::kTRAINING)
return;
// Add one history tab per player
for (unsigned int i = 0; i < m_game->getNPlayers(); ++i)
{
const Player &player = m_game->getPlayer(i);
HistoryWidget *h = new HistoryWidget(NULL);
h->setHistory(&player.getHistory(), true);
h->setHistory(&player.getHistory(), m_game, true);
QObject::connect(this, SIGNAL(refreshSignal()), h, SLOT(refresh()));
addTab(h, qfw(player.getName()));
}

View file

@ -37,7 +37,8 @@ class HistoryWidget: public QTreeView
public:
explicit HistoryWidget(QWidget *parent = 0);
void setHistory(const History *iHistory = NULL,
void setHistory(const History *iHistory,
const Game *iGame = NULL,
bool iIsForPlayer = false);
public slots:
@ -47,9 +48,12 @@ private:
/// Encapsulated history, can be NULL
const History *m_history;
/// Corresponding game (used to retrieve the players names) can be NULL
const Game *m_game;
/**
* Flag to avoid displaying the "players" column when the History object
* is precisely associated to a Player
* is associated to a Player
*/
bool m_forPlayer;

View file

@ -25,6 +25,7 @@
#include <QtGui/QMessageBox>
#include <QtGui/QFileDialog>
#include <QtGui/QDockWidget>
#include <QtCore/QSettings>
#include "main_window.h"
#include "dic.h"
@ -55,6 +56,8 @@ MainWindow::MainWindow(QWidget *iParent)
m_prefsDialog(NULL), m_bagWindow(NULL)
{
m_ui.setupUi(this);
QObject::connect(this, SIGNAL(gameChanged(const Game*)),
this, SLOT(updateForGame(const Game*)));
// Board
BoardWidget *boardWidget = new BoardWidget;
@ -102,14 +105,28 @@ MainWindow::MainWindow(QWidget *iParent)
scores, SLOT(refresh()));
m_ui.groupBoxPlayers->layout()->addWidget(scores);
// XXX: temporary, for testing purposes!
try
emit gameChangedNonConst(NULL);
emit gameChanged(NULL);
// Load dictionary
QSettings qs;
QString dicPath = qs.value(PrefsDialog::kINTF_DIC_PATH, "").toString();
// FIXME: the messages are not displayed anymore when the window is shown
if (dicPath == "")
{
m_dic = new Dictionary("/home/ipkiss/ods5.dawg");
displayInfoMsg(_q("No dictionary selected"));
}
catch (...)
else
{
// Ignore the error silently :)
try
{
m_dic = new Dictionary(qtl(dicPath));
}
catch (...)
{
displayInfoMsg(_q("No dictionary selected"));
displayErrorMsg(_q("Cannot load dictionary '%1' indicated in the preferences").arg(dicPath));
}
}
}
@ -139,6 +156,32 @@ void MainWindow::destroyCurrentGame()
}
void MainWindow::updateForGame(const Game *iGame)
{
if (iGame == NULL)
{
m_ui.action_GameSaveAs->setEnabled(false);
setWindowTitle(_q("No game") + " - Eliot");
}
else
{
m_ui.action_GameSaveAs->setEnabled(true);
if (iGame->getMode() == Game::kTRAINING)
{
setWindowTitle(_q("Training mode") + " - Eliot");
}
else if (iGame->getMode() == Game::kDUPLICATE)
{
setWindowTitle(_q("Duplicate game") + " - Eliot");
}
else
{
setWindowTitle(_q("Free game") + " - Eliot");
}
}
}
void MainWindow::displayErrorMsg(QString iMsg, QString iContext)
{
if (iContext == "")
@ -148,6 +191,12 @@ void MainWindow::displayErrorMsg(QString iMsg, QString iContext)
}
void MainWindow::displayInfoMsg(QString iMsg)
{
statusBar()->showMessage(iMsg);
}
void MainWindow::on_action_GameNew_triggered()
{
if (m_dic == NULL)
@ -177,6 +226,7 @@ void MainWindow::on_action_GameNew_triggered()
emit gameChangedNonConst(m_game);
emit gameChanged(m_game);
emit gameUpdated();
displayInfoMsg(_q("Game started"));
}
@ -202,6 +252,7 @@ void MainWindow::on_action_GameLoad_triggered()
emit gameChangedNonConst(m_game);
emit gameChanged(m_game);
emit gameUpdated();
displayInfoMsg(_q("Game loaded"));
}
}
@ -216,6 +267,7 @@ void MainWindow::on_action_GameSaveAs_triggered()
{
ofstream fout(qtl(fileName));
m_game->save(fout);
displayInfoMsg(_q("Game saved"));
}
}
@ -223,23 +275,45 @@ void MainWindow::on_action_GameSaveAs_triggered()
void MainWindow::on_action_SettingsPreferences_triggered()
{
if (m_prefsDialog == NULL)
{
m_prefsDialog = new PrefsDialog(this);
QObject::connect(m_prefsDialog, SIGNAL(gameUpdated()),
this, SIGNAL(gameUpdated()));
}
m_prefsDialog->exec();
}
void MainWindow::on_action_SettingsChooseDic_triggered()
{
if (m_game)
{
int res = QMessageBox::question(this, _q("Stop current game?"),
_q("Loading a dictionary will stop the current game. Do you want to continue?"),
QMessageBox::Yes | QMessageBox::Default,
QMessageBox::No | QMessageBox::Escape);
if (res == QMessageBox::No)
return;
}
QString fileName =
QFileDialog::getOpenFileName(this, _q("Choose a dictionary"), "", "*.dawg");
if (!fileName.isEmpty())
{
destroyCurrentGame();
try
{
Dictionary *dic = new Dictionary(qtl(fileName));
delete m_dic;
m_dic = dic;
emit dicChanged(fileName, qfw(m_dic->getHeader().getName()));
displayInfoMsg(QString("Loaded dictionary '%1'").arg(fileName));
// Save the location of the dictionary in the preferences
QSettings qs;
QString dicPath = qs.value(PrefsDialog::kINTF_DIC_PATH, "").toString();
qs.setValue(PrefsDialog::kINTF_DIC_PATH, fileName);
}
catch (std::exception &e)
{

View file

@ -52,6 +52,7 @@ signals:
public slots:
/// Display an error message to the user
void displayErrorMsg(QString iMsg, QString iContext = "");
void displayInfoMsg(QString iMsg);
private slots:
void on_action_GameNew_triggered();
@ -62,6 +63,12 @@ private slots:
void on_action_WindowsBag_triggered();
void on_action_HelpAbout_triggered();
/**
* Perform several updates when the game changes (title bar, status bar,
* grey out some menu items, ...)
*/
void updateForGame(const Game *iGame);
private:
/// Current dictionary
const Dictionary *m_dic;

View file

@ -92,11 +92,24 @@ Game * NewGame::createGame(const Dictionary &iDic) const
// Add the players
if (comboBoxMode->currentText() != _q("Training"))
{
set<QString> allNames;
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);
// Ensure unicity of the players names
if (allNames.find(name) != allNames.end())
{
int n = 2;
while (allNames.find(name + QString(" (%1)").arg(n)) != allNames.end())
{
++n;
}
name += QString(" (%1)").arg(n);
}
allNames.insert(name);
QString type = m_model->data(m_model->index(num, 1)).toString();
if (type == kHUMAN)
game->addHumanPlayer();

View file

@ -255,7 +255,7 @@ void PlayerTabWidget::setGame(Game *iGame)
int nbTabs = count();
for (int i = 0; i < nbTabs; ++i)
{
setCurrentWidget(0);
setCurrentIndex(0);
// Cut all the connections with the page (needed because removeTab()
// doesn't really destroy the widget)
disconnect(currentWidget());

View file

@ -19,13 +19,14 @@
*****************************************************************************/
#include <QtCore/QSettings>
#include <QtGui/QFileDialog>
#include "prefs_dialog.h"
#include "settings.h"
const QString PrefsDialog::kINTF_ALIGN_HISTORY = "AlignHistory";
const QString PrefsDialog::kINTF_ALIGN_HISTORY = "Interface/AlignHistory";
const QString PrefsDialog::kINTF_DIC_PATH = "Interface/DicPath";
PrefsDialog::PrefsDialog(QWidget *iParent)
@ -35,12 +36,8 @@ PrefsDialog::PrefsDialog(QWidget *iParent)
// Interface settings
QSettings qs;
qs.beginGroup("Interface");
checkBoxIntfAlignHistory->setChecked(qs.value(kINTF_ALIGN_HISTORY).toBool());
qs.endGroup();
// XXX: Hide the Interface settings until the "align history" is really
// taken into account by the HistoryWidget class
groupBoxInterface->hide();
lineEditIntfDicPath->setText(qs.value(kINTF_DIC_PATH, "").toString());
// Duplicate settings
checkBoxDuplRefuseInvalid->setChecked(Settings::Instance().getBool("duplicate-reject-invalid"));
@ -68,11 +65,17 @@ void PrefsDialog::accept()
void PrefsDialog::updateSettings()
{
bool shouldEmitUpdate = false;
// Interface settings
QSettings qs;
qs.beginGroup("Interface");
qs.setValue(kINTF_ALIGN_HISTORY, checkBoxIntfAlignHistory->isChecked());
qs.endGroup();
if (qs.value(kINTF_ALIGN_HISTORY).toBool() != checkBoxIntfAlignHistory->isChecked())
{
// We need to redraw the history widget
shouldEmitUpdate = true;
qs.setValue(kINTF_ALIGN_HISTORY, checkBoxIntfAlignHistory->isChecked());
}
qs.setValue(kINTF_DIC_PATH, lineEditIntfDicPath->text());
// Duplicate settings
Settings::Instance().setBool("duplicate-reject-invalid",
@ -87,6 +90,18 @@ void PrefsDialog::updateSettings()
checkBoxFreeRefuseInvalid->isChecked());
// Training settings
if (shouldEmitUpdate)
emit gameUpdated();
}
void PrefsDialog::on_pushButtonIntfDicBrowse_clicked()
{
QString fileName =
QFileDialog::getOpenFileName(this, _q("Choose a dictionary"), "", "*.dawg");
if (fileName != "")
lineEditIntfDicPath->setText(fileName);
}

View file

@ -35,11 +35,18 @@ public:
explicit PrefsDialog(QWidget *iParent = 0);
static const QString kINTF_ALIGN_HISTORY;
static const QString kINTF_DIC_PATH;
public slots:
/// Update the settings when the user selects "OK"
virtual void accept();
signals:
void gameUpdated();
private slots:
void on_pushButtonIntfDicBrowse_clicked();
private:
void updateSettings();

View file

@ -5,8 +5,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>343</width>
<height>383</height>
<width>365</width>
<height>418</height>
</rect>
</property>
<property name="windowTitle" >
@ -19,8 +19,52 @@
<string>_("Interface")</string>
</property>
<layout class="QVBoxLayout" >
<item>
<layout class="QHBoxLayout" >
<item>
<widget class="QLabel" name="label_4" >
<property name="text" >
<string>_("Dictionary path:")</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="lineEditIntfDicPath" >
<property name="toolTip" >
<string>_("Enter the dictionary path (mandatory to start a game)")</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonIntfDicBrowse" >
<property name="toolTip" >
<string>_("Open a browser window to choose the dictionary")</string>
</property>
<property name="text" >
<string>_("Browse...")</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkBoxIntfAlignHistory" >
<property name="toolTip" >
<string>_("If checked, the game and player histories will diaplay the rack and the corresponding solution on the same line")</string>
</property>
<property name="text" >
<string>_("Align the rack and the solution in history")</string>
</property>