From 82b5694a59c65e4b0335fc6d0af21d00e92db889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Teuli=C3=A8re?= Date: Sun, 25 Mar 2012 01:21:20 +0100 Subject: [PATCH] Split the ArbitrationWidget class into 2 classes, to keep the code manageable --- qt/Makefile.am | 4 + qt/arbit_assignments.cpp | 601 ++++++++++++++++++++++++++++++++++++ qt/arbit_assignments.h | 108 +++++++ qt/arbitration_widget.cpp | 559 +++------------------------------ qt/arbitration_widget.h | 32 +- qt/ui/arbit_assignments.ui | 150 +++++++++ qt/ui/arbitration_widget.ui | 148 +-------- 7 files changed, 912 insertions(+), 690 deletions(-) create mode 100644 qt/arbit_assignments.cpp create mode 100644 qt/arbit_assignments.h create mode 100644 qt/ui/arbit_assignments.ui diff --git a/qt/Makefile.am b/qt/Makefile.am index cb206af..08a16aa 100644 --- a/qt/Makefile.am +++ b/qt/Makefile.am @@ -45,6 +45,7 @@ EXTRA_DIST = \ ui/dic_wizard_letters_def_page.ui \ ui/dic_wizard_conclusion_page.ui \ ui/arbitration_widget.ui \ + ui/arbit_assignments.ui \ ui/bag_widget.ui \ ui/main_window.ui \ ui/new_game.ui \ @@ -67,6 +68,7 @@ eliot_SOURCES = \ misc_helpers.cpp misc_helpers.h \ custom_popup.cpp custom_popup.h \ arbitration_widget.cpp arbitration_widget.h \ + arbit_assignments.cpp arbit_assignments.h \ bag_widget.cpp bag_widget.h \ bag_widget2.cpp bag_widget2.h \ dic_tools_widget.cpp dic_tools_widget.h \ @@ -88,6 +90,7 @@ eliot_SOURCES = \ nodist_eliot_SOURCES = \ ui/main_window.ui.h \ ui/arbitration_widget.ui.h \ + ui/arbit_assignments.ui.h \ ui/bag_widget.ui.h \ ui/new_game.ui.h \ ui/player_widget.ui.h \ @@ -110,6 +113,7 @@ nodist_eliot_SOURCES = \ new_game.moc.cpp \ dic_tools_widget.moc.cpp \ arbitration_widget.moc.cpp \ + arbit_assignments.moc.cpp \ bag_widget.moc.cpp \ bag_widget2.moc.cpp \ score_widget.moc.cpp \ diff --git a/qt/arbit_assignments.cpp b/qt/arbit_assignments.cpp new file mode 100644 index 0000000..04b2695 --- /dev/null +++ b/qt/arbit_assignments.cpp @@ -0,0 +1,601 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2012 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 "arbit_assignments.h" +#include "qtcommon.h" +#include "custom_popup.h" +#include "misc_helpers.h" + +#include "public_game.h" +#include "player.h" +#include "rack.h" +#include "results.h" +#include "debug.h" + +using namespace std; + + +INIT_LOGGER(qt, ArbitAssignments); + + +ArbitAssignments::ArbitAssignments(QWidget *parent, PublicGame *iGame) + : QWidget(parent), m_game(iGame) +{ + setupUi(this); + + // Associate a model to the players view. + // We use a proxy, to enable easy sorting of the players. + m_proxyPlayersModel = new QSortFilterProxyModel(this); + m_proxyPlayersModel->setDynamicSortFilter(true); + m_playersModel = new QStandardItemModel(this); + m_proxyPlayersModel->setSourceModel(m_playersModel); + treeViewPlayers->setModel(m_proxyPlayersModel); + m_playersModel->setColumnCount(4); + m_playersModel->setHeaderData(0, Qt::Horizontal, _q("Table"), Qt::DisplayRole); + m_playersModel->setHeaderData(1, Qt::Horizontal, _q("Player"), Qt::DisplayRole); + m_playersModel->setHeaderData(2, Qt::Horizontal, _q("Word"), Qt::DisplayRole); + m_playersModel->setHeaderData(3, Qt::Horizontal, _q("Ref"), Qt::DisplayRole); + m_playersModel->setHeaderData(4, Qt::Horizontal, _q("Points"), Qt::DisplayRole); + treeViewPlayers->sortByColumn(0, Qt::AscendingOrder); + + treeViewPlayers->setColumnWidth(0, 70); + treeViewPlayers->setColumnWidth(1, 160); + treeViewPlayers->setColumnWidth(2, 160); + treeViewPlayers->setColumnWidth(3, 40); + treeViewPlayers->setColumnWidth(4, 50); + + KeyEventFilter *filter = new KeyEventFilter(this, Qt::Key_T); + QObject::connect(filter, SIGNAL(keyPressed(int, int)), + this, SLOT(assignTopMove())); + treeViewPlayers->installEventFilter(filter); + + filter = new KeyEventFilter(this, Qt::Key_W); + QObject::connect(filter, SIGNAL(keyPressed(int, int)), + this, SLOT(addRemoveWarning())); + treeViewPlayers->installEventFilter(filter); + + filter = new KeyEventFilter(this, Qt::Key_P); + QObject::connect(filter, SIGNAL(keyPressed(int, int)), + this, SLOT(addPenalty())); + treeViewPlayers->installEventFilter(filter); + + // Display a preview of the master word when clicked + QObject::connect(labelMasterMove, SIGNAL(clicked()), + this, SLOT(showMasterPreview())); + + // Enable the assignment buttons according to the selections in trees + QObject::connect(treeViewPlayers->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, SLOT(enableAssignmentButtons())); + + // Move assignment + QObject::connect(buttonSelectMaster, SIGNAL(clicked()), + this, SLOT(assignMasterMove())); + QObject::connect(buttonNoMove, SIGNAL(clicked()), + this, SLOT(assignNoMove())); + QObject::connect(buttonAssign, SIGNAL(clicked()), + this, SLOT(assignSelectedMove())); + QObject::connect(treeViewPlayers, SIGNAL(activated(const QModelIndex&)), + this, SLOT(assignSelectedMove())); + + // End turn + QObject::connect(buttonEndTurn, SIGNAL(clicked()), + this, SLOT(endTurn())); + + // Add a context menu for the players + CustomPopup *playersPopup = new CustomPopup(treeViewPlayers); + QObject::connect(playersPopup, SIGNAL(popupCreated(QMenu&, const QPoint&)), + this, SLOT(populatePlayersMenu(QMenu&, const QPoint&))); + + refresh(); +} + + +void ArbitAssignments::refresh() +{ + updatePlayersModel(); + + // Update the master move + const Move &masterMove = m_game->duplicateGetMasterMove(); + if (masterMove.getType() == Move::NO_MOVE) + { + labelMasterMove->setText(QString("%1").arg(_q("Not selected yet"))); + } + else + { + QString label = QString("%1").arg(formatMove(masterMove)); + labelMasterMove->setText(label); + } + + if (m_game->isFinished()) + { + setEnabled(false); + } +} + + +void ArbitAssignments::updatePlayersModel() +{ + // Save the ID of the selected players + QSet playersIdSet = getSelectedPlayers(); + + m_playersModel->removeRows(0, m_playersModel->rowCount()); + if (m_game == NULL) + return; + + const bool hideAssignedPlayers = checkBoxHideAssigned->isChecked(); + for (unsigned int i = 0; i < m_game->getNbPlayers(); ++i) + { + const Player &player = m_game->getPlayer(i); + if (hideAssignedPlayers && m_game->hasPlayed(player.getId())) + continue; + // Only display human players + if (!player.isHuman()) + continue; + const int rowNum = m_playersModel->rowCount(); + m_playersModel->insertRow(rowNum); + m_playersModel->setData(m_playersModel->index(rowNum, 0), player.getTableNb()); + m_playersModel->setData(m_playersModel->index(rowNum, 1), qfw(player.getName())); + // Store the player ID + m_playersModel->setData(m_playersModel->index(rowNum, 0), + player.getId(), Qt::UserRole); + // Display the played word, if any + if (m_game->hasPlayed(player.getId())) + { + const Move &move = player.getLastMove(); + m_playersModel->setData(m_playersModel->index(rowNum, 4), move.getScore()); + + QPalette palette = treeViewPlayers->palette(); + QColor color = palette.color(QPalette::Normal, QPalette::WindowText); + if (move.getType() == Move::VALID_ROUND) + { + const Round &round = move.getRound(); + m_playersModel->setData(m_playersModel->index(rowNum, 2), qfw(round.getWord())); + m_playersModel->setData(m_playersModel->index(rowNum, 3), qfw(round.getCoord().toString())); + } + else if (move.getType() == Move::NO_MOVE) + { + m_playersModel->setData(m_playersModel->index(rowNum, 2), _q("(NO MOVE)")); + color = Qt::blue; + } + else if (move.getType() == Move::INVALID_WORD) + { + m_playersModel->setData(m_playersModel->index(rowNum, 2), "<" + qfw(move.getBadWord()) + ">"); + m_playersModel->setData(m_playersModel->index(rowNum, 3), qfw(move.getBadCoord())); + color = Qt::red; + } + // Apply the color + const QBrush brush(color); + m_playersModel->setData(m_playersModel->index(rowNum, 2), + brush, Qt::ForegroundRole); + m_playersModel->setData(m_playersModel->index(rowNum, 3), + brush, Qt::ForegroundRole); + } + // Restore the selection + if (playersIdSet.contains(player.getId())) + { + LOG_DEBUG("selecting player " << player.getId()); + treeViewPlayers->selectionModel()->select(m_playersModel->index(rowNum, 0), + QItemSelectionModel::Select | QItemSelectionModel::Rows); + } + } +} + + +void ArbitAssignments::enableAssignmentButtons() +{ + bool hasSelResult = m_game->isLastTurn() && + m_selectedMove.getType() != Move::NO_MOVE; + bool hasSelPlayer = m_game->isLastTurn() && + treeViewPlayers->selectionModel()->hasSelection(); + // Enable the "Assign move" button iff a move is selected + // and at least one player in the tree view is selected + buttonAssign->setEnabled(hasSelResult && hasSelPlayer); + if (hasSelResult && hasSelPlayer) + { + const Move &move = m_selectedMove; + buttonAssign->setToolTip(_q("Assign move (%1) to the selected player(s)") + .arg(formatMove(move))); + } + buttonNoMove->setEnabled(hasSelPlayer); + buttonSelectMaster->setEnabled(hasSelResult); +} + + +void ArbitAssignments::populatePlayersMenu(QMenu &iMenu, const QPoint &iPoint) +{ + const QModelIndex &index = treeViewPlayers->indexAt(iPoint); + if (!index.isValid()) + return; + + // Action to assign the selected move + if (m_selectedMove.getType() != Move::NO_MOVE) + { + const Move &move = m_selectedMove; + QAction *assignSelMoveAction = + new QAction(_q("Assign selected move (%1)").arg(formatMove(move)), this); + assignSelMoveAction->setStatusTip(_q("Assign move (%1) to the selected player(s)") + .arg(formatMove(move))); + assignSelMoveAction->setShortcut(Qt::Key_Enter); + QObject::connect(assignSelMoveAction, SIGNAL(triggered()), + this, SLOT(assignSelectedMove())); + iMenu.addAction(assignSelMoveAction); + } + + // Action to assign the top move + QAction *assignTopMoveAction = new QAction(_q("Assign top move"), this); + assignTopMoveAction->setStatusTip(_q("Assign the top move (if unique) to the selected player(s)")); + assignTopMoveAction->setShortcut(Qt::Key_T); + QObject::connect(assignTopMoveAction, SIGNAL(triggered()), + this, SLOT(assignTopMove())); + iMenu.addAction(assignTopMoveAction); + + // Action to warn (or "unwarn") players + QAction *warningAction = new QAction(_q("Give (or remove) a warning"), this); + warningAction->setStatusTip(_q("Give a warning to the selected player(s), or remove it if they already have one")); + warningAction->setShortcut(Qt::Key_W); + QObject::connect(warningAction, SIGNAL(triggered()), + this, SLOT(addRemoveWarning())); + iMenu.addAction(warningAction); + + // Action to give a penalty to players + QAction *penaltyAction = new QAction(_q("Give a penalty"), this); + penaltyAction->setStatusTip(_q("Give a penalty to the selected player(s)")); + penaltyAction->setShortcut(Qt::Key_P); + QObject::connect(penaltyAction, SIGNAL(triggered()), + this, SLOT(addPenalty())); + iMenu.addAction(penaltyAction); +} + + +void ArbitAssignments::on_checkBoxHideAssigned_toggled(bool) +{ + treeViewPlayers->selectionModel()->clearSelection(); + updatePlayersModel(); +} + + +void ArbitAssignments::selectedMoveChanged(const Move &iMove) +{ + m_selectedMove = iMove; + enableAssignmentButtons(); +} + + +bool ArbitAssignments::hasSelectedPlayer() const +{ + return treeViewPlayers->selectionModel()->hasSelection(); +} + + +QSet ArbitAssignments::getSelectedPlayers() const +{ + QSet playersIdSet; + + // Get the tree selection + const QItemSelection &proxySelected = treeViewPlayers->selectionModel()->selection(); + // Map the selection to a source model index + const QItemSelection &srcSelected = m_proxyPlayersModel->mapSelectionToSource(proxySelected); + // Get the player ID for each line + Q_FOREACH(const QModelIndex &index, srcSelected.indexes()) + { + // Only take the first column into account + if (index.column() != 0) + continue; + playersIdSet.insert(m_playersModel->data(index, Qt::UserRole).toUInt()); + } + + return playersIdSet; +} + + +bool ArbitAssignments::selectPlayerByTable(unsigned tabNb, QString *oName) +{ + // Unselect all the players + treeViewPlayers->selectionModel()->clearSelection(); + for (int rowNum = 0; rowNum < m_playersModel->rowCount(); ++rowNum) + { + const QModelIndex &modelIndex = m_playersModel->index(rowNum, 0); + if (m_playersModel->data(modelIndex).toUInt() == tabNb) + { + // Found! + const QModelIndex &index = m_proxyPlayersModel->mapFromSource(modelIndex); + treeViewPlayers->scrollTo(index); + treeViewPlayers->selectionModel()->select(index, + QItemSelectionModel::Select | QItemSelectionModel::Rows); + *oName = m_playersModel->data(m_playersModel->index(rowNum, 1)).toString(); + return true; + } + } + return false; +} + + +void ArbitAssignments::showMasterPreview() +{ + const Move &move = m_game->duplicateGetMasterMove(); + if (move.getType() == Move::VALID_ROUND) + { + // TODO: deselect move in the Results? + m_game->setTestRound(move.getRound()); + emit gameUpdated(); + } +} + + +Rack ArbitAssignments::getRack() const +{ + return m_game->getHistory().getCurrentRack().getRack(); +} + + +void ArbitAssignments::assignMasterMove() +{ + if (m_game->isFinished()) + return; + + const Move &masterMove = m_game->duplicateGetMasterMove(); + // Make sure the user knows what she's doing + if (masterMove.getType() != Move::NO_MOVE) + { + QString msg = _q("There is already a master move for this turn."); + QString question = _q("Do you want to replace it?"); + if (!QtCommon::requestConfirmation(msg, question)) + return; + } + + const Move &move = m_selectedMove; + if (move.getType() != Move::VALID_ROUND) + { + notifyProblem(_q("The master move must be a valid move.")); + return; + } + + // Warn if the selected move is not a top move + BestResults results; + results.search(m_game->getDic(), m_game->getBoard(), + getRack(), m_game->getHistory().beforeFirstRound()); + ASSERT(results.size() != 0, "No possible valid move"); + int bestScore = results.get(0).getPoints(); + if (bestScore > move.getScore()) + { + QString msg = _q("The selected move scores less than the maximum."); + QString question = _q("Do you really want to select it as master move?"); + if (!QtCommon::requestConfirmation(msg, question)) + return; + } + else + { + // A top move saving a joker should be prefered over one not + // saving it, according to the French official rules. + // Warn if the user tries to ignore the rule. + unsigned jokerCount = move.getRound().countJokersFromRack(); + if (jokerCount > 0) + { + for (unsigned i = 0; i < results.size(); ++i) + { + if (results.get(i).countJokersFromRack() < jokerCount) + { + QString msg = _q("The selected move uses more jokers than " + "another move with the same score (%1).") + .arg(formatMove(Move(results.get(i)))); + QString question = _q("Do you really want to select it as master move?"); + if (!QtCommon::requestConfirmation(msg, question)) + return; + break; + } + } + } + } + + // Assign the master move + m_game->duplicateSetMasterMove(move); + emit gameUpdated(); +} + + +void ArbitAssignments::assignDefaultMasterMove() +{ + const Move &currMove = m_game->duplicateGetMasterMove(); + // Do not overwrite an existing move + if (currMove.getType() != Move::NO_MOVE) + return; + + // Search the best moves + BestResults results; + results.search(m_game->getDic(), m_game->getBoard(), + getRack(), m_game->getHistory().beforeFirstRound()); + // XXX: End of game + if (results.size() == 0) + return; + + unsigned currIndex = 0; + unsigned jokerCount = results.get(0).countJokersFromRack(); + if (jokerCount > 0) + { + for (unsigned i = 1; i < results.size(); ++i) + { + if (results.get(i).countJokersFromRack() < jokerCount) + { + currIndex = i; + jokerCount = results.get(i).countJokersFromRack(); + } + } + } + + // Assign the master move + Move move = Move(results.get(currIndex)); + m_game->duplicateSetMasterMove(move); + emit gameUpdated(); +} + + +void ArbitAssignments::assignSelectedMove() +{ + if (m_selectedMove.getType() == Move::NO_MOVE || + !treeViewPlayers->selectionModel()->hasSelection()) + { + return; + } + helperAssignMove(m_selectedMove); +} + + +void ArbitAssignments::assignTopMove() +{ + BestResults results; + results.search(m_game->getDic(), m_game->getBoard(), + getRack(), m_game->getHistory().beforeFirstRound()); + // TODO: what if there are several moves? + if (results.size() == 1) + helperAssignMove(Move(results.get(0))); +} + + +void ArbitAssignments::assignNoMove() +{ + helperAssignMove(Move()); +} + + +void ArbitAssignments::helperAssignMove(const Move &iMove) +{ + QSet playersIdSet = getSelectedPlayers(); + + // Warn if some of the selected players already have an assigned move + QSet assignedIdSet; + BOOST_FOREACH(unsigned int id, playersIdSet) + { + if (m_game->hasPlayed(id)) + assignedIdSet.insert(id); + } + if (!assignedIdSet.empty()) + { + QString msg = _q("The following players already have an assigned move:\n"); + BOOST_FOREACH(unsigned int id, assignedIdSet) + { + msg += QString("\t%1\n").arg(qfw(m_game->getPlayer(id).getName())); + } + QString question = _q("Do you want to replace it?"); + if (!QtCommon::requestConfirmation(msg, question)) + return; + } + + // Assign the move to each selected player + BOOST_FOREACH(unsigned int id, playersIdSet) + { + LOG_DEBUG(lfq(QString("Assigning move %1 to player %2") + .arg(qfw(iMove.toString())).arg(id))); + + // Assign the move + m_game->arbitrationAssign(id, iMove); + } + emit notifyInfo(_q("Move assigned to player(s)")); + emit gameUpdated(); +} + + +void ArbitAssignments::addRemoveWarning() +{ + QSet playersIdSet = getSelectedPlayers(); + if (playersIdSet.isEmpty()) + return; + + BOOST_FOREACH(unsigned int id, playersIdSet) + { + m_game->arbitrationToggleWarning(id); + } + emit gameUpdated(); +} + + +void ArbitAssignments::addPenalty() +{ + QSet playersIdSet = getSelectedPlayers(); + if (playersIdSet.isEmpty()) + return; + + BOOST_FOREACH(unsigned int id, playersIdSet) + { + m_game->arbitrationAddPenalty(id, 0); + } + emit gameUpdated(); +} + + +QString ArbitAssignments::formatMove(const Move &iMove) const +{ + if (iMove.getType() == Move::VALID_ROUND) + { + return QString("%1 - %2 - %3") + .arg(qfw(iMove.getRound().getWord())) + .arg(qfw(iMove.getRound().getCoord().toString())) + .arg(iMove.getScore()); + } + else + { + ASSERT(iMove.getType() == Move::INVALID_WORD, "Unexpected move type"); + return QString("%1 - %2 - %3") + .arg(qfw(iMove.getBadWord())) + .arg(qfw(iMove.getBadCoord())) + .arg(iMove.getScore()); + } +} + + +void ArbitAssignments::endTurn() +{ + if (m_game->duplicateGetMasterMove().getType() != Move::VALID_ROUND) + { + notifyProblem(_q("You must select a master move before ending the turn.")); + return; + } + + bool allPlayed = true; + for (unsigned int i = 0; i < m_game->getNbPlayers(); ++i) + { + if (!m_game->hasPlayed(i) && m_game->getPlayer(i).isHuman()) + allPlayed = false; + } + if (!allPlayed) + { + QString msg = _q("Some player(s) have no assigned move. " + "If you continue, they will be assigned a \"(NO MOVE)\" " + "pseudo-move, but you will be able to change that later."); + if (!QtCommon::requestConfirmation(msg)) + return; + } + + emit notifyInfo(_q("New turn started")); + + m_game->removeTestRound(); + m_game->arbitrationFinalizeTurn(); + // FIXME: shouldn't be done here + setEnabled(!m_game->isFinished()); + + emit gameUpdated(); +} + diff --git a/qt/arbit_assignments.h b/qt/arbit_assignments.h new file mode 100644 index 0000000..b22f1f1 --- /dev/null +++ b/qt/arbit_assignments.h @@ -0,0 +1,108 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2012 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 ARBIT_ASSIGNMENTS_H_ +#define ARBIT_ASSIGNMENTS_H_ + +#include + +#include +#include "move.h" +#include "logging.h" + +class PublicGame; +class QStandardItemModel; +class QSortFilterProxyModel; +class QMenu; +class QPoint; +class QString; + +class ArbitAssignments: public QWidget, private Ui::ArbitAssignments +{ + Q_OBJECT; + DEFINE_LOGGER(); + +public: + ArbitAssignments(QWidget *parent, PublicGame *iGame); + + /// Format the given move under the format WORD - Ref - Pts + QString formatMove(const Move &iMove) const; + + /** + * Select the player with the given table number. + * Return true if the player was found, false otherwise. + * If the player is found, the oName parameter is filled + * with the player name. + */ + bool selectPlayerByTable(unsigned tabNb, QString *oName); + + bool hasSelectedPlayer() const; + +signals: + void gameUpdated(); + void notifyProblem(QString iMsg); + void notifyInfo(QString iMsg); + +public slots: + void refresh(); + void enableAssignmentButtons(); + void assignMasterMove(); + void assignSelectedMove(); + void assignDefaultMasterMove(); + void selectedMoveChanged(const Move&); + +private slots: + void on_checkBoxHideAssigned_toggled(bool); + void showMasterPreview(); + void populatePlayersMenu(QMenu &iMenu, const QPoint &iPoint); + void assignTopMove(); + void assignNoMove(); + void addRemoveWarning(); + void addPenalty(); + void endTurn(); + +private: + /// Encapsulated game, can be NULL + PublicGame *m_game; + + /// Model for the players list + QStandardItemModel *m_playersModel; + /// Proxy for the players model + QSortFilterProxyModel *m_proxyPlayersModel; + + // Move currently selected (in the Results table) + Move m_selectedMove; + + /// Force synchronizing the model with the players + void updatePlayersModel(); + + /// Return the rack for the current turn + // FIXME: this feature should be provided by the core + Rack getRack() const; + + /// Helper method to return the ID of the selected player(s) + QSet getSelectedPlayers() const; + + /// Helper method to assign the given move to the selected player(s) + void helperAssignMove(const Move &iMove); +}; + +#endif + diff --git a/qt/arbitration_widget.cpp b/qt/arbitration_widget.cpp index 0135797..d692e77 100644 --- a/qt/arbitration_widget.cpp +++ b/qt/arbitration_widget.cpp @@ -18,17 +18,13 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *****************************************************************************/ -#include -#include - #include #include -#include #include -#include #include #include "arbitration_widget.h" +#include "arbit_assignments.h" #include "qtcommon.h" #include "prefs_dialog.h" #include "validator_factory.h" @@ -38,7 +34,6 @@ #include "public_game.h" #include "player.h" -#include "pldrack.h" #include "rack.h" #include "results.h" #include "coord_model.h" @@ -63,13 +58,20 @@ ArbitrationWidget::ArbitrationWidget(QWidget *parent, { setupUi(this); + m_assignmentsWidget = new ArbitAssignments(this, iGame); + layoutAssignments->addWidget(m_assignmentsWidget); + QObject::connect(m_assignmentsWidget, SIGNAL(gameUpdated()), + this, SIGNAL(gameUpdated())); + QObject::connect(m_assignmentsWidget, SIGNAL(notifyProblem(QString)), + this, SIGNAL(notifyProblem(QString))); + QObject::connect(m_assignmentsWidget, SIGNAL(notifyInfo(QString)), + this, SIGNAL(notifyInfo(QString))); + m_keyAccum = new KeyAccumulator(this, 400); - // FIXME arbitration begin // The players widget uses more space by default splitter->setStretchFactor(0, 1); splitter->setStretchFactor(1, 2); - // FIXME arbitration end blackPalette = lineEditRack->palette(); redPalette = lineEditRack->palette(); @@ -82,42 +84,6 @@ ArbitrationWidget::ArbitrationWidget(QWidget *parent, lineEditRack->setValidator(val); lineEditCoords->setValidator(ValidatorFactory::newCoordsValidator(this)); - // Associate a model to the players view. - // We use a proxy, to enable easy sorting of the players. - m_proxyPlayersModel = new QSortFilterProxyModel(this); - m_proxyPlayersModel->setDynamicSortFilter(true); - m_playersModel = new QStandardItemModel(this); - m_proxyPlayersModel->setSourceModel(m_playersModel); - treeViewPlayers->setModel(m_proxyPlayersModel); - m_playersModel->setColumnCount(4); - m_playersModel->setHeaderData(0, Qt::Horizontal, _q("Table"), Qt::DisplayRole); - m_playersModel->setHeaderData(1, Qt::Horizontal, _q("Player"), Qt::DisplayRole); - m_playersModel->setHeaderData(2, Qt::Horizontal, _q("Word"), Qt::DisplayRole); - m_playersModel->setHeaderData(3, Qt::Horizontal, _q("Ref"), Qt::DisplayRole); - m_playersModel->setHeaderData(4, Qt::Horizontal, _q("Points"), Qt::DisplayRole); - treeViewPlayers->sortByColumn(0, Qt::AscendingOrder); - - treeViewPlayers->setColumnWidth(0, 70); - treeViewPlayers->setColumnWidth(1, 160); - treeViewPlayers->setColumnWidth(2, 160); - treeViewPlayers->setColumnWidth(3, 40); - treeViewPlayers->setColumnWidth(4, 50); - - KeyEventFilter *filter = new KeyEventFilter(this, Qt::Key_T); - QObject::connect(filter, SIGNAL(keyPressed(int, int)), - this, SLOT(assignTopMove())); - treeViewPlayers->installEventFilter(filter); - - filter = new KeyEventFilter(this, Qt::Key_W); - QObject::connect(filter, SIGNAL(keyPressed(int, int)), - this, SLOT(addRemoveWarning())); - treeViewPlayers->installEventFilter(filter); - - filter = new KeyEventFilter(this, Qt::Key_P); - QObject::connect(filter, SIGNAL(keyPressed(int, int)), - this, SLOT(addPenalty())); - treeViewPlayers->installEventFilter(filter); - // Associate a model to the results view. // We use a proxy, to enable easy sorting/filtering of the results. m_proxyResultsModel = new QSortFilterProxyModel(this); @@ -143,7 +109,7 @@ ArbitrationWidget::ArbitrationWidget(QWidget *parent, KeyEventFilter *masterFilter = new KeyEventFilter(this, Qt::Key_M); QObject::connect(masterFilter, SIGNAL(keyPressed(int, int)), - this, SLOT(assignMasterMove())); + m_assignmentsWidget, SLOT(assignMasterMove())); treeViewResults->installEventFilter(masterFilter); KeyEventFilter *numFilter = new KeyEventFilter(this, Qt::Key_0); @@ -179,21 +145,20 @@ ArbitrationWidget::ArbitrationWidget(QWidget *parent, QObject::connect(treeViewResults->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SLOT(showPreview(const QItemSelection&))); - // Display a preview of the master word when clicked - QObject::connect(labelMasterMove, SIGNAL(clicked()), - this, SLOT(showMasterPreview())); // Dynamic filter for search results QObject::connect(lineEditFilter, SIGNAL(textChanged(const QString&)), this, SLOT(resultsFilterChanged(const QString&))); // Enable the assignment buttons according to the selections in trees - QObject::connect(treeViewPlayers->selectionModel(), - SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), - this, SLOT(enableAssignmentButtons())); QObject::connect(treeViewResults->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), - this, SLOT(enableAssignmentButtons())); + m_assignmentsWidget, SLOT(enableAssignmentButtons())); + + // Enable the assignment buttons according to the selections in trees + QObject::connect(treeViewResults->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, SLOT(updateSelectedMove())); // Coordinates model QObject::connect(&m_coordModel, SIGNAL(coordChanged(const Coord&, const Coord&)), @@ -216,20 +181,8 @@ ArbitrationWidget::ArbitrationWidget(QWidget *parent, this, SLOT(checkWord())); // Move assignment - QObject::connect(buttonSelectMaster, SIGNAL(clicked()), - this, SLOT(assignMasterMove())); - QObject::connect(buttonNoMove, SIGNAL(clicked()), - this, SLOT(assignNoMove())); - QObject::connect(buttonAssign, SIGNAL(clicked()), - this, SLOT(assignSelectedMove())); QObject::connect(treeViewResults, SIGNAL(activated(const QModelIndex&)), - this, SLOT(assignSelectedMove())); - QObject::connect(treeViewPlayers, SIGNAL(activated(const QModelIndex&)), - this, SLOT(assignSelectedMove())); - - // End turn - QObject::connect(buttonEndTurn, SIGNAL(clicked()), - this, SLOT(endTurn())); + m_assignmentsWidget, SLOT(assignSelectedMove())); // Add a context menu for the results m_resultsPopup = new CustomPopup(treeViewResults); @@ -237,10 +190,6 @@ ArbitrationWidget::ArbitrationWidget(QWidget *parent, this, SLOT(populateResultsMenu(QMenu&, const QPoint&))); QObject::connect(m_resultsPopup, SIGNAL(requestDefinition(QString)), this, SIGNAL(requestDefinition(QString))); - // Add a context menu for the players - CustomPopup *playersPopup = new CustomPopup(treeViewPlayers); - QObject::connect(playersPopup, SIGNAL(popupCreated(QMenu&, const QPoint&)), - this, SLOT(populatePlayersMenu(QMenu&, const QPoint&))); refresh(); } @@ -258,19 +207,7 @@ void ArbitrationWidget::refresh() } updateResultsModel(); - updatePlayersModel(); - - // Update the master move - const Move &masterMove = m_game->duplicateGetMasterMove(); - if (masterMove.getType() == Move::NO_MOVE) - { - labelMasterMove->setText(QString("%1").arg(_q("Not selected yet"))); - } - else - { - QString label = QString("%1").arg(formatMove(masterMove)); - labelMasterMove->setText(label); - } + m_assignmentsWidget->refresh(); // The rack can only be changed at the last turn bool isLastTurn = m_game->isLastTurn(); @@ -327,74 +264,6 @@ void ArbitrationWidget::updateResultsModel() } -void ArbitrationWidget::updatePlayersModel() -{ - // Save the ID of the selected players - QSet playersIdSet = getSelectedPlayers(); - - m_playersModel->removeRows(0, m_playersModel->rowCount()); - if (m_game == NULL) - return; - - const bool hideAssignedPlayers = checkBoxHideAssigned->isChecked(); - for (unsigned int i = 0; i < m_game->getNbPlayers(); ++i) - { - const Player &player = m_game->getPlayer(i); - if (hideAssignedPlayers && m_game->hasPlayed(player.getId())) - continue; - // Only display human players - if (!player.isHuman()) - continue; - const int rowNum = m_playersModel->rowCount(); - m_playersModel->insertRow(rowNum); - m_playersModel->setData(m_playersModel->index(rowNum, 0), player.getTableNb()); - m_playersModel->setData(m_playersModel->index(rowNum, 1), qfw(player.getName())); - // Store the player ID - m_playersModel->setData(m_playersModel->index(rowNum, 0), - player.getId(), Qt::UserRole); - // Display the played word, if any - if (m_game->hasPlayed(player.getId())) - { - const Move &move = player.getLastMove(); - m_playersModel->setData(m_playersModel->index(rowNum, 4), move.getScore()); - - QPalette palette = treeViewPlayers->palette(); - QColor color = palette.color(QPalette::Normal, QPalette::WindowText); - if (move.getType() == Move::VALID_ROUND) - { - const Round &round = move.getRound(); - m_playersModel->setData(m_playersModel->index(rowNum, 2), qfw(round.getWord())); - m_playersModel->setData(m_playersModel->index(rowNum, 3), qfw(round.getCoord().toString())); - } - else if (move.getType() == Move::NO_MOVE) - { - m_playersModel->setData(m_playersModel->index(rowNum, 2), _q("(NO MOVE)")); - color = Qt::blue; - } - else if (move.getType() == Move::INVALID_WORD) - { - m_playersModel->setData(m_playersModel->index(rowNum, 2), "<" + qfw(move.getBadWord()) + ">"); - m_playersModel->setData(m_playersModel->index(rowNum, 3), qfw(move.getBadCoord())); - color = Qt::red; - } - // Apply the color - const QBrush brush(color); - m_playersModel->setData(m_playersModel->index(rowNum, 2), - brush, Qt::ForegroundRole); - m_playersModel->setData(m_playersModel->index(rowNum, 3), - brush, Qt::ForegroundRole); - } - // Restore the selection - if (playersIdSet.contains(player.getId())) - { - LOG_DEBUG("selecting player " << player.getId()); - treeViewPlayers->selectionModel()->select(m_playersModel->index(rowNum, 0), - QItemSelectionModel::Select | QItemSelectionModel::Rows); - } - } -} - - int ArbitrationWidget::addSingleMove(const Move &iMove, int moveType, unsigned int index, int bestScore) { @@ -564,26 +433,6 @@ void ArbitrationWidget::enableCheckWordButton() } -void ArbitrationWidget::enableAssignmentButtons() -{ - bool hasSelResult = m_game->isLastTurn() && - treeViewResults->selectionModel()->hasSelection(); - bool hasSelPlayer = m_game->isLastTurn() && - treeViewPlayers->selectionModel()->hasSelection(); - // Enable the "Assign move" button iff a move is selected - // and at least one player in the tree view is selected - buttonAssign->setEnabled(hasSelResult && hasSelPlayer); - if (hasSelResult && hasSelPlayer) - { - const Move &move = getSelectedMove(); - buttonAssign->setToolTip(_q("Assign move (%1) to the selected player(s)") - .arg(formatMove(move))); - } - buttonNoMove->setEnabled(hasSelPlayer); - buttonSelectMaster->setEnabled(hasSelResult); -} - - void ArbitrationWidget::on_buttonSearch_clicked() { m_game->removeTestRound(); @@ -596,7 +445,7 @@ void ArbitrationWidget::on_buttonSearch_clicked() QSettings settings; if (settings.value(PrefsDialog::kARBIT_AUTO_MASTER, false).toBool()) { - assignDefaultMasterMove(); + m_assignmentsWidget->assignDefaultMasterMove(); } } @@ -624,7 +473,7 @@ void ArbitrationWidget::populateResultsMenu(QMenu &iMenu, const QPoint &iPoint) .arg(formatMove(move))); setAsMasterAction->setShortcut(Qt::Key_M); QObject::connect(setAsMasterAction, SIGNAL(triggered()), - this, SLOT(assignMasterMove())); + m_assignmentsWidget, SLOT(assignMasterMove())); iMenu.addAction(setAsMasterAction); if (move.getType() != Move::VALID_ROUND) setAsMasterAction->setEnabled(false); @@ -636,61 +485,13 @@ void ArbitrationWidget::populateResultsMenu(QMenu &iMenu, const QPoint &iPoint) .arg(formatMove(move))); assignSelMoveAction->setShortcut(Qt::Key_Enter); QObject::connect(assignSelMoveAction, SIGNAL(triggered()), - this, SLOT(assignSelectedMove())); + m_assignmentsWidget, SLOT(assignSelectedMove())); iMenu.addAction(assignSelMoveAction); - if (!treeViewPlayers->selectionModel()->hasSelection()) + if (!m_assignmentsWidget->hasSelectedPlayer()) assignSelMoveAction->setEnabled(false); } -void ArbitrationWidget::populatePlayersMenu(QMenu &iMenu, const QPoint &iPoint) -{ - const QModelIndex &index = treeViewPlayers->indexAt(iPoint); - if (!index.isValid()) - return; - - // Action to assign the selected move - if (treeViewResults->selectionModel()->hasSelection()) - { - const Move &move = getSelectedMove(); - QAction *assignSelMoveAction = - new QAction(_q("Assign selected move (%1)").arg(formatMove(move)), this); - assignSelMoveAction->setStatusTip(_q("Assign move (%1) to the selected player(s)") - .arg(formatMove(move))); - assignSelMoveAction->setShortcut(Qt::Key_Enter); - QObject::connect(assignSelMoveAction, SIGNAL(triggered()), - this, SLOT(assignSelectedMove())); - iMenu.addAction(assignSelMoveAction); - } - - // Action to assign the top move - QAction *assignTopMoveAction = new QAction(_q("Assign top move"), this); - assignTopMoveAction->setStatusTip(_q("Assign the top move (if unique) to the selected player(s)")); - assignTopMoveAction->setShortcut(Qt::Key_T); - QObject::connect(assignTopMoveAction, SIGNAL(triggered()), - this, SLOT(assignTopMove())); - iMenu.addAction(assignTopMoveAction); - - // Action to warn (or "unwarn") players - QAction *warningAction = new QAction(_q("Give (or remove) a warning"), this); - warningAction->setStatusTip(_q("Give a warning to the selected player(s), or remove it if they already have one")); - warningAction->setShortcut(Qt::Key_W); - QObject::connect(warningAction, SIGNAL(triggered()), - this, SLOT(addRemoveWarning())); - iMenu.addAction(warningAction); - - // Action to give a penalty to players - QAction *penaltyAction = new QAction(_q("Give a penalty"), this); - penaltyAction->setStatusTip(_q("Give a penalty to the selected player(s)")); - penaltyAction->setShortcut(Qt::Key_P); - QObject::connect(penaltyAction, SIGNAL(triggered()), - this, SLOT(addPenalty())); - iMenu.addAction(penaltyAction); - - // TODO: add other actions -} - - void ArbitrationWidget::checkWord() { if (lineEditWord->text().isEmpty() || @@ -755,34 +556,28 @@ void ArbitrationWidget::selectTableNumber(int key) // Select the player with this table number LOG_DEBUG("Selecting player with table number: " + lfq(tableNum)); - treeViewPlayers->selectionModel()->clearSelection(); - unsigned tabNb = tableNum.toUInt(); - for (int rowNum = 0; rowNum < m_playersModel->rowCount(); ++rowNum) + QString name; + bool found = m_assignmentsWidget->selectPlayerByTable(tableNum.toUInt(), &name); + if (found) { - const QModelIndex &modelIndex = m_playersModel->index(rowNum, 0); - if (m_playersModel->data(modelIndex).toUInt() == tabNb) - { - const QModelIndex &index = m_proxyPlayersModel->mapFromSource(modelIndex); - treeViewPlayers->scrollTo(index); - treeViewPlayers->selectionModel()->select(index, - QItemSelectionModel::Select | QItemSelectionModel::Rows); - // Keep the focus in the results view - treeViewResults->setFocus(); + // Keep the focus in the results view + treeViewResults->setFocus(); - // Write a nice message on the status bar - QString name = m_playersModel->data(m_playersModel->index(rowNum, 1)).toString(); - emit notifyInfo(_q("Player at table %1 selected (%2)").arg(tabNb).arg(name)); - return; - } + // Write a nice message on the status bar + emit notifyInfo(_q("Player at table %1 selected (%2)").arg(tableNum).arg(name)); + return; } LOG_DEBUG("Not found"); } -void ArbitrationWidget::on_checkBoxHideAssigned_toggled(bool) +void ArbitrationWidget::updateSelectedMove() { - treeViewPlayers->selectionModel()->clearSelection(); - updatePlayersModel(); + bool hasSelection = treeViewResults->selectionModel()->hasSelection(); + if (hasSelection) + m_assignmentsWidget->selectedMoveChanged(getSelectedMove()); + else + m_assignmentsWidget->selectedMoveChanged(Move()); } @@ -814,31 +609,11 @@ Move ArbitrationWidget::getSelectedMove() const } -QSet ArbitrationWidget::getSelectedPlayers() const -{ - QSet playersIdSet; - - // Get the tree selection - const QItemSelection &proxySelected = treeViewPlayers->selectionModel()->selection(); - // Map the selection to a source model index - const QItemSelection &srcSelected = m_proxyPlayersModel->mapSelectionToSource(proxySelected); - // Get the player ID for each line - Q_FOREACH(const QModelIndex &index, srcSelected.indexes()) - { - // Only take the first column into account - if (index.column() != 0) - continue; - playersIdSet.insert(m_playersModel->data(index, Qt::UserRole).toUInt()); - } - - return playersIdSet; -} - - void ArbitrationWidget::clearResults() { m_game->removeTestRound(); m_results.clear(); + m_addedMoves.clear(); } @@ -857,18 +632,6 @@ void ArbitrationWidget::showPreview(const QItemSelection &iSelected) } -void ArbitrationWidget::showMasterPreview() -{ - const Move &move = m_game->duplicateGetMasterMove(); - if (move.getType() == Move::VALID_ROUND) - { - treeViewResults->clearSelection(); - m_game->setTestRound(move.getRound()); - } - emit gameUpdated(); -} - - Rack ArbitrationWidget::getRack() const { return m_game->getHistory().getCurrentRack().getRack(); @@ -886,252 +649,8 @@ int ArbitrationWidget::getBestScore() const } -void ArbitrationWidget::assignMasterMove() -{ - if (m_game->isFinished()) - return; - - const Move &masterMove = m_game->duplicateGetMasterMove(); - // Make sure the user knows what she's doing - if (masterMove.getType() != Move::NO_MOVE) - { - QString msg = _q("There is already a master move for this turn."); - QString question = _q("Do you want to replace it?"); - if (!QtCommon::requestConfirmation(msg, question)) - return; - } - - const Move &move = getSelectedMove(); - if (move.getType() != Move::VALID_ROUND) - { - notifyProblem(_q("The master move must be a valid move.")); - return; - } - - // Warn if the selected move is not a top move - BestResults results; - results.search(m_game->getDic(), m_game->getBoard(), - getRack(), m_game->getHistory().beforeFirstRound()); - ASSERT(results.size() != 0, "No possible valid move"); - int bestScore = results.get(0).getPoints(); - if (bestScore > move.getScore()) - { - QString msg = _q("The selected move scores less than the maximum."); - QString question = _q("Do you really want to select it as master move?"); - if (!QtCommon::requestConfirmation(msg, question)) - return; - } - else - { - // A top move saving a joker should be prefered over one not - // saving it, according to the French official rules. - // Warn if the user tries to ignore the rule. - unsigned jokerCount = move.getRound().countJokersFromRack(); - if (jokerCount > 0) - { - for (unsigned i = 0; i < results.size(); ++i) - { - if (results.get(i).countJokersFromRack() < jokerCount) - { - QString msg = _q("The selected move uses more jokers than " - "another move with the same score (%1).") - .arg(formatMove(Move(results.get(i)))); - QString question = _q("Do you really want to select it as master move?"); - if (!QtCommon::requestConfirmation(msg, question)) - return; - break; - } - } - } - } - - // Assign the master move - m_game->duplicateSetMasterMove(move); - emit gameUpdated(); -} - - -void ArbitrationWidget::assignDefaultMasterMove() -{ - const Move &currMove = m_game->duplicateGetMasterMove(); - // Do not overwrite an existing move - if (currMove.getType() != Move::NO_MOVE) - return; - - // Search the best moves - BestResults results; - results.search(m_game->getDic(), m_game->getBoard(), - getRack(), m_game->getHistory().beforeFirstRound()); - // XXX: End of game - if (results.size() == 0) - return; - - unsigned currIndex = 0; - unsigned jokerCount = results.get(0).countJokersFromRack(); - if (jokerCount > 0) - { - for (unsigned i = 1; i < results.size(); ++i) - { - if (results.get(i).countJokersFromRack() < jokerCount) - { - currIndex = i; - jokerCount = results.get(i).countJokersFromRack(); - } - } - } - - // Assign the master move - Move move = Move(results.get(currIndex)); - m_game->duplicateSetMasterMove(move); - emit gameUpdated(); -} - - -void ArbitrationWidget::assignSelectedMove() -{ - if (!treeViewResults->selectionModel()->hasSelection() || - !treeViewPlayers->selectionModel()->hasSelection()) - { - return; - } - helperAssignMove(getSelectedMove()); -} - - -void ArbitrationWidget::assignTopMove() -{ - BestResults results; - results.search(m_game->getDic(), m_game->getBoard(), - getRack(), m_game->getHistory().beforeFirstRound()); - // TODO: what if there are several moves? - if (results.size() == 1) - helperAssignMove(Move(results.get(0))); -} - - -void ArbitrationWidget::assignNoMove() -{ - helperAssignMove(Move()); -} - - -void ArbitrationWidget::helperAssignMove(const Move &iMove) -{ - QSet playersIdSet = getSelectedPlayers(); - - // Warn if some of the selected players already have an assigned move - QSet assignedIdSet; - BOOST_FOREACH(unsigned int id, playersIdSet) - { - if (m_game->hasPlayed(id)) - assignedIdSet.insert(id); - } - if (!assignedIdSet.empty()) - { - QString msg = _q("The following players already have an assigned move:\n"); - BOOST_FOREACH(unsigned int id, assignedIdSet) - { - msg += QString("\t%1\n").arg(qfw(m_game->getPlayer(id).getName())); - } - QString question = _q("Do you want to replace it?"); - if (!QtCommon::requestConfirmation(msg, question)) - return; - } - - // Assign the move to each selected player - BOOST_FOREACH(unsigned int id, playersIdSet) - { - LOG_DEBUG(lfq(QString("Assigning move %1 to player %2") - .arg(qfw(iMove.toString())).arg(id))); - - // Assign the move - m_game->arbitrationAssign(id, iMove); - } - emit notifyInfo(_q("Move assigned to player(s)")); - emit gameUpdated(); -} - - -void ArbitrationWidget::addRemoveWarning() -{ - QSet playersIdSet = getSelectedPlayers(); - if (playersIdSet.isEmpty()) - return; - - BOOST_FOREACH(unsigned int id, playersIdSet) - { - m_game->arbitrationToggleWarning(id); - } - emit gameUpdated(); -} - - -void ArbitrationWidget::addPenalty() -{ - QSet playersIdSet = getSelectedPlayers(); - if (playersIdSet.isEmpty()) - return; - - BOOST_FOREACH(unsigned int id, playersIdSet) - { - m_game->arbitrationAddPenalty(id, 0); - } - emit gameUpdated(); -} - - QString ArbitrationWidget::formatMove(const Move &iMove) const { - if (iMove.getType() == Move::VALID_ROUND) - { - return QString("%1 - %2 - %3") - .arg(qfw(iMove.getRound().getWord())) - .arg(qfw(iMove.getRound().getCoord().toString())) - .arg(iMove.getScore()); - } - else - { - ASSERT(iMove.getType() == Move::INVALID_WORD, "Unexpected move type"); - return QString("%1 - %2 - %3") - .arg(qfw(iMove.getBadWord())) - .arg(qfw(iMove.getBadCoord())) - .arg(iMove.getScore()); - } -} - - -void ArbitrationWidget::endTurn() -{ - if (m_game->duplicateGetMasterMove().getType() != Move::VALID_ROUND) - { - notifyProblem(_q("You must select a master move before ending the turn.")); - return; - } - - bool allPlayed = true; - for (unsigned int i = 0; i < m_game->getNbPlayers(); ++i) - { - if (!m_game->hasPlayed(i) && m_game->getPlayer(i).isHuman()) - allPlayed = false; - } - if (!allPlayed) - { - QString msg = _q("Some player(s) have no assigned move. " - "If you continue, they will be assigned a \"(NO MOVE)\" " - "pseudo-move, but you will be able to change that later."); - if (!QtCommon::requestConfirmation(msg)) - return; - } - - m_addedMoves.clear(); - - emit notifyInfo(_q("New turn started")); - - m_game->removeTestRound(); - m_game->arbitrationFinalizeTurn(); - // FIXME: shouldn't be done here - setEnabled(!m_game->isFinished()); - - emit gameUpdated(); + return m_assignmentsWidget->formatMove(iMove); } diff --git a/qt/arbitration_widget.h b/qt/arbitration_widget.h index c7b5e52..667369d 100644 --- a/qt/arbitration_widget.h +++ b/qt/arbitration_widget.h @@ -29,6 +29,7 @@ #include "logging.h" class PublicGame; +class ArbitAssignments; class CoordModel; class CustomPopup; class KeyAccumulator; @@ -36,7 +37,6 @@ class QStandardItemModel; class QSortFilterProxyModel; class QMenu; class QPoint; -class QKeyEvent; class ArbitrationWidget: public QWidget, private Ui::ArbitrationWidget { @@ -62,32 +62,24 @@ private slots: void setRackRandom(); void rackEdited(const QString &); void on_buttonSearch_clicked(); - void on_checkBoxHideAssigned_toggled(bool); void resultsFilterChanged(const QString &); void enableCheckWordButton(); - void enableAssignmentButtons(); + void checkWord(); void clearResults(); + void updateSelectedMove(); void showPreview(const QItemSelection &); - void showMasterPreview(); void updateCoordText(const Coord&, const Coord&); void updateCoordModel(const QString&); void populateResultsMenu(QMenu &iMenu, const QPoint &iPoint); - void populatePlayersMenu(QMenu &iMenu, const QPoint &iPoint); - void checkWord(); void selectTableNumber(int key); - void assignMasterMove(); - void assignDefaultMasterMove(); - void assignSelectedMove(); - void assignTopMove(); - void assignNoMove(); - void addRemoveWarning(); - void addPenalty(); - void endTurn(); private: /// Encapsulated game, can be NULL PublicGame *m_game; + /// Assignments widget (right part of the splitter) + ArbitAssignments * m_assignmentsWidget; + /// Coordinates of the next word to play CoordModel &m_coordModel; @@ -99,11 +91,6 @@ private: /// Proxy for the results model QSortFilterProxyModel *m_proxyResultsModel; - /// Model for the players list - QStandardItemModel *m_playersModel; - /// Proxy for the players model - QSortFilterProxyModel *m_proxyPlayersModel; - /// Popup menu for the search results CustomPopup *m_resultsPopup; @@ -121,8 +108,6 @@ private: /// Force synchronizing the model with the search results void updateResultsModel(); - /// Force synchronizing the model with the players - void updatePlayersModel(); /** * Add the given move to the results list. @@ -139,11 +124,6 @@ private: /// Helper method to return a structured move for the selected result Move getSelectedMove() const; - /// Helper method to return the ID of the selected player(s) - QSet getSelectedPlayers() const; - - /// Helper method to assign the given move to the selected player(s) - void helperAssignMove(const Move &iMove); /// Format the given move under the format WORD - Ref - Pts QString formatMove(const Move &iMove) const; diff --git a/qt/ui/arbit_assignments.ui b/qt/ui/arbit_assignments.ui new file mode 100644 index 0000000..f65281f --- /dev/null +++ b/qt/ui/arbit_assignments.ui @@ -0,0 +1,150 @@ + + + ArbitAssignments + + + + 0 + 0 + 404 + 300 + + + + Form + + + + + + _("Assignments") + + + + + + + + _("Master move:") + + + + + + + + 0 + 0 + + + + + + + + + + + false + + + _("Select") + + + + + + + + + _("Hide players with an assigned move") + + + + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::ExtendedSelection + + + 0 + + + true + + + true + + + + + + + + + false + + + _("Assign move") + + + + + + + false + + + _("Indicate that the selected player(s) did not play") + + + _("No move") + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + _("Validate the current turn and start a new one") + + + _("End turn") + + + + + + + + + + + + + ClickableLabel + QLabel +
misc_helpers.h
+
+
+ + +
diff --git a/qt/ui/arbitration_widget.ui b/qt/ui/arbitration_widget.ui index e668e35..78bced3 100644 --- a/qt/ui/arbitration_widget.ui +++ b/qt/ui/arbitration_widget.ui @@ -6,8 +6,8 @@ 0 0 - 839 - 292 + 761 + 312 @@ -35,7 +35,7 @@ - 80 + 90 0 @@ -157,146 +157,12 @@ - - - - - _("Assignments") - - - - - - - - _("Master move:") - - - - - - - - 0 - 0 - - - - - - - - - - - false - - - _("Select") - - - - - - - - - _("Hide players with an assigned move") - - - - - - - QAbstractItemView::NoEditTriggers - - - true - - - QAbstractItemView::ExtendedSelection - - - 0 - - - true - - - true - - - - - - - - - - - false - - - _("Assign move") - - - - - - - false - - - _("Indicate that the selected player(s) did not play") - - - _("No move") - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - _("Validate the current turn and start a new one") - - - _("End turn") - - - - - - - - - - - + - - - ClickableLabel - QLabel -
misc_helpers.h
-
-
lineEditRack buttonRandom @@ -306,12 +172,6 @@ lineEditWord lineEditCoords buttonCheck - buttonSelectMaster - checkBoxHideAssigned - treeViewPlayers - buttonAssign - buttonNoMove - buttonEndTurn