From 457b368ae70ce0f44026fcee7d0ab1356b941d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Teuli=C3=A8re?= Date: Sat, 26 Jan 2008 16:58:46 +0000 Subject: [PATCH] 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 --- qt/board_widget.cpp | 13 +++++-- qt/board_widget.h | 1 + qt/history_widget.cpp | 63 +++++++++++++++++++++++--------- qt/history_widget.h | 8 +++-- qt/main_window.cpp | 84 ++++++++++++++++++++++++++++++++++++++++--- qt/main_window.h | 7 ++++ qt/new_game.cpp | 13 +++++++ qt/player_widget.cpp | 2 +- qt/prefs_dialog.cpp | 35 ++++++++++++------ qt/prefs_dialog.h | 7 ++++ qt/ui/prefs_dialog.ui | 48 +++++++++++++++++++++++-- 11 files changed, 242 insertions(+), 39 deletions(-) diff --git a/qt/board_widget.cpp b/qt/board_widget.cpp index 72547bb..40cc039 100644 --- a/qt/board_widget.cpp +++ b/qt/board_widget.cpp @@ -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))); } } } diff --git a/qt/board_widget.h b/qt/board_widget.h index 8785774..465b6ba 100644 --- a/qt/board_widget.h +++ b/qt/board_widget.h @@ -56,6 +56,7 @@ private: static const QColor W2Colour; static const QColor W3Colour; static const QColor TileColour; + static const QColor PreviewColour; //@} }; diff --git a/qt/history_widget.cpp b/qt/history_widget.cpp index 65235ac..393c1a9 100644 --- a/qt/history_widget.cpp +++ b/qt/history_widget.cpp @@ -22,8 +22,10 @@ #include #include #include +#include #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())); } diff --git a/qt/history_widget.h b/qt/history_widget.h index 5ab3ebe..fdca50e 100644 --- a/qt/history_widget.h +++ b/qt/history_widget.h @@ -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; diff --git a/qt/main_window.cpp b/qt/main_window.cpp index 33d3679..53af2a6 100644 --- a/qt/main_window.cpp +++ b/qt/main_window.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #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) { diff --git a/qt/main_window.h b/qt/main_window.h index 001e9e3..0d7cd94 100644 --- a/qt/main_window.h +++ b/qt/main_window.h @@ -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; diff --git a/qt/new_game.cpp b/qt/new_game.cpp index ba87f59..2ad0a20 100644 --- a/qt/new_game.cpp +++ b/qt/new_game.cpp @@ -92,11 +92,24 @@ Game * NewGame::createGame(const Dictionary &iDic) const // Add the players if (comboBoxMode->currentText() != _q("Training")) { + set 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(); diff --git a/qt/player_widget.cpp b/qt/player_widget.cpp index 7fe400b..65268af 100644 --- a/qt/player_widget.cpp +++ b/qt/player_widget.cpp @@ -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()); diff --git a/qt/prefs_dialog.cpp b/qt/prefs_dialog.cpp index 0a08231..6c66837 100644 --- a/qt/prefs_dialog.cpp +++ b/qt/prefs_dialog.cpp @@ -19,13 +19,14 @@ *****************************************************************************/ #include +#include #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); +} + diff --git a/qt/prefs_dialog.h b/qt/prefs_dialog.h index 51f84cc..60be091 100644 --- a/qt/prefs_dialog.h +++ b/qt/prefs_dialog.h @@ -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(); diff --git a/qt/ui/prefs_dialog.ui b/qt/ui/prefs_dialog.ui index 096816c..75640a1 100644 --- a/qt/ui/prefs_dialog.ui +++ b/qt/ui/prefs_dialog.ui @@ -5,8 +5,8 @@ 0 0 - 343 - 383 + 365 + 418 @@ -19,8 +19,52 @@ _("Interface") + + + + + + _("Dictionary path:") + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + _("Enter the dictionary path (mandatory to start a game)") + + + + + + + _("Open a browser window to choose the dictionary") + + + _("Browse...") + + + + + + + _("If checked, the game and player histories will diaplay the rack and the corresponding solution on the same line") + _("Align the rack and the solution in history")