--- a/qmlfrontend/CMakeLists.txt Fri May 24 22:27:19 2019 +0200
+++ b/qmlfrontend/CMakeLists.txt Fri May 24 23:39:51 2019 +0200
@@ -18,6 +18,8 @@
"engine_interface.h"
"preview_acceptor.cpp" "preview_acceptor.h"
"net_session.cpp" "net_session.h"
+ "players_model.cpp" "players_model.h"
+ "rooms_model.cpp" "rooms_model.h"
)
target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Network Qt5::Quick)
--- a/qmlfrontend/net_session.cpp Fri May 24 22:27:19 2019 +0200
+++ b/qmlfrontend/net_session.cpp Fri May 24 23:39:51 2019 +0200
@@ -1,6 +1,14 @@
#include "net_session.h"
-NetSession::NetSession(QObject *parent) : QObject(parent) {}
+#include "players_model.h"
+#include "rooms_model.h"
+
+NetSession::NetSession(QObject *parent)
+ : QObject(parent),
+ m_playersModel(new PlayersListModel()),
+ m_roomsModel(new RoomsListModel()) {}
+
+NetSession::~NetSession() { close(); }
QUrl NetSession::url() const { return m_url; }
@@ -31,6 +39,8 @@
return m_sessionState;
}
+QString NetSession::room() const { return m_room; }
+
void NetSession::setUrl(const QUrl &url) {
if (m_url == url) return;
@@ -52,12 +62,20 @@
emit passwordChanged(m_password);
}
+void NetSession::setRoom(const QString &room) {
+ if (m_room == room) return;
+
+ m_room = room;
+ emit roomChanged(m_room);
+}
+
void NetSession::close() {
if (!m_socket.isNull()) {
m_socket->disconnectFromHost();
m_socket.clear();
setSessionState(NotConnected);
+ setRoom({});
}
}
@@ -182,9 +200,33 @@
void NetSession::handleLeft(const QStringList ¶meters) {}
-void NetSession::handleLobbyJoined(const QStringList ¶meters) {}
+void NetSession::handleLobbyJoined(const QStringList ¶meters) {
+ for (auto player : parameters) {
+ if (player == m_nickname) {
+ // check if server is authenticated or no authentication was performed at
+ // all
+ if (!m_serverAuthHash.isEmpty()) {
+ emit error(tr("Server authentication error"));
+
+ close();
+ }
+
+ setSessionState(Lobby);
+ }
-void NetSession::handleLobbyLeft(const QStringList ¶meters) {}
+ m_playersModel->addPlayer(player, false);
+ }
+}
+
+void NetSession::handleLobbyLeft(const QStringList ¶meters) {
+ if (parameters.length() == 1) {
+ m_playersModel->removePlayer(parameters[0]);
+ } else if (parameters.length() == 2) {
+ m_playersModel->removePlayer(parameters[0], parameters[1]);
+ } else {
+ qWarning("Malformed LOBBY:LEFT message");
+ }
+}
void NetSession::handleNick(const QStringList ¶meters) {
if (parameters.length()) setNickname(parameters[0]);
@@ -206,9 +248,29 @@
void NetSession::handleRoomAbandoned(const QStringList ¶meters) {}
-void NetSession::handleRoom(const QStringList ¶meters) {}
+void NetSession::handleRoom(const QStringList ¶meters) {
+ if (parameters.size() == 10 && parameters[0] == "ADD") {
+ m_roomsModel->addRoom(parameters.mid(1));
+ } else if (parameters.length() == 11 && parameters[0] == "UPD") {
+ m_roomsModel->updateRoom(parameters[1], parameters.mid(2));
-void NetSession::handleRooms(const QStringList ¶meters) {}
+ // keep track of room name so correct name is displayed
+ if (m_room == parameters[1]) {
+ setRoom(parameters[2]);
+ }
+ } else if (parameters.size() == 2 && parameters[0] == "DEL") {
+ m_roomsModel->removeRoom(parameters[1]);
+ }
+}
+
+void NetSession::handleRooms(const QStringList ¶meters) {
+ if (parameters.size() % 9) {
+ qWarning("Net: Malformed ROOMS message");
+ return;
+ }
+
+ m_roomsModel->setRoomsList(parameters);
+}
void NetSession::handleRoundFinished(const QStringList ¶meters) {}
--- a/qmlfrontend/net_session.h Fri May 24 22:27:19 2019 +0200
+++ b/qmlfrontend/net_session.h Fri May 24 23:39:51 2019 +0200
@@ -6,6 +6,8 @@
#include <QStringList>
#include <QUrl>
+class PlayersListModel;
+class RoomsListModel;
class NetSession : public QObject {
Q_OBJECT
@@ -18,6 +20,7 @@
Q_PROPERTY(QString nickname READ nickname WRITE setNickname NOTIFY nicknameChanged)
Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)
Q_PROPERTY(SessionState sessionState READ sessionState NOTIFY sessionStateChanged)
+ Q_PROPERTY(QString room READ room NOTIFY roomChanged)
// clang-format on
public:
@@ -25,21 +28,23 @@
Q_ENUMS(SessionState)
explicit NetSession(QObject *parent = nullptr);
+ ~NetSession() override;
QUrl url() const;
QAbstractSocket::SocketState state() const;
- Q_INVOKABLE void open();
-
QString nickname() const;
QString password() const;
SessionState sessionState() const;
+ QString room() const;
public slots:
+ void open();
+ void close();
+
void setUrl(const QUrl &url);
void setNickname(const QString &nickname);
void setPassword(const QString &password);
- void close();
signals:
void urlChanged(const QUrl url);
@@ -49,6 +54,7 @@
void sessionStateChanged(SessionState sessionState);
void warning(const QString &message);
void error(const QString &message);
+ void roomChanged(const QString &room);
private slots:
void onReadyRead();
@@ -97,14 +103,21 @@
void send(const QString &message, const QStringList ¶meters);
void send(const QStringList &message);
void setSessionState(SessionState sessionState);
+ void setRoom(const QString &room);
private:
QUrl m_url;
QSharedPointer<QTcpSocket> m_socket;
+ QSharedPointer<PlayersListModel> m_playersModel;
+ QSharedPointer<RoomsListModel> m_roomsModel;
QString m_nickname;
QString m_password;
QStringList m_buffer;
SessionState m_sessionState;
+ QString m_serverAuthHash;
+ QString m_room;
+
+ Q_DISABLE_COPY(NetSession)
};
#endif // NET_SESSION_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlfrontend/players_model.cpp Fri May 24 23:39:51 2019 +0200
@@ -0,0 +1,364 @@
+#include <QDebug>
+#include <QFile>
+#include <QModelIndex>
+#include <QModelIndexList>
+#include <QPainter>
+#include <QTextStream>
+
+#include "players_model.h"
+
+PlayersListModel::PlayersListModel(QObject *parent)
+ : QAbstractListModel(parent) {
+ m_fontInRoom = QFont();
+ m_fontInRoom.setItalic(true);
+}
+
+int PlayersListModel::rowCount(const QModelIndex &parent) const {
+ if (parent.isValid())
+ return 0;
+ else
+ return m_data.size();
+}
+
+QVariant PlayersListModel::data(const QModelIndex &index, int role) const {
+ if (!index.isValid() || index.row() < 0 || index.row() >= rowCount() ||
+ index.column() != 0)
+ return QVariant(QVariant::Invalid);
+
+ return m_data.at(index.row()).value(role);
+}
+
+bool PlayersListModel::setData(const QModelIndex &index, const QVariant &value,
+ int role) {
+ if (!index.isValid() || index.row() < 0 || index.row() >= rowCount() ||
+ index.column() != 0)
+ return false;
+
+ m_data[index.row()].insert(role, value);
+
+ emit dataChanged(index, index);
+
+ return true;
+}
+
+bool PlayersListModel::insertRows(int row, int count,
+ const QModelIndex &parent) {
+ if (parent.isValid() || row > rowCount() || row < 0 || count < 1)
+ return false;
+
+ beginInsertRows(parent, row, row + count - 1);
+
+ for (int i = 0; i < count; ++i) m_data.insert(row, DataEntry());
+
+ endInsertRows();
+
+ return true;
+}
+
+bool PlayersListModel::removeRows(int row, int count,
+ const QModelIndex &parent) {
+ if (parent.isValid() || row + count > rowCount() || row < 0 || count < 1)
+ return false;
+
+ beginRemoveRows(parent, row, row + count - 1);
+
+ for (int i = 0; i < count; ++i) m_data.removeAt(row);
+
+ endRemoveRows();
+
+ return true;
+}
+
+QModelIndex PlayersListModel::nicknameIndex(const QString &nickname) {
+ QModelIndexList mil =
+ match(index(0), Qt::DisplayRole, nickname, 1, Qt::MatchExactly);
+
+ if (mil.size() > 0)
+ return mil[0];
+ else
+ return QModelIndex();
+}
+
+void PlayersListModel::addPlayer(const QString &nickname, bool notify) {
+ insertRow(rowCount());
+
+ QModelIndex mi = index(rowCount() - 1);
+ setData(mi, nickname);
+
+ checkFriendIgnore(mi);
+
+ emit nickAddedLobby(nickname, notify);
+}
+
+void PlayersListModel::removePlayer(const QString &nickname,
+ const QString &msg) {
+ if (msg.isEmpty())
+ emit nickRemovedLobby(nickname, QString());
+ else
+ emit nickRemovedLobby(nickname, msg);
+
+ QModelIndex mi = nicknameIndex(nickname);
+
+ if (mi.isValid()) removeRow(mi.row());
+}
+
+void PlayersListModel::playerJoinedRoom(const QString &nickname, bool notify) {
+ QModelIndex mi = nicknameIndex(nickname);
+
+ if (mi.isValid()) {
+ setData(mi, true, RoomFilterRole);
+ updateIcon(mi);
+ updateSortData(mi);
+ }
+
+ emit nickAdded(nickname, notify);
+}
+
+void PlayersListModel::playerLeftRoom(const QString &nickname) {
+ emit nickRemoved(nickname);
+
+ QModelIndex mi = nicknameIndex(nickname);
+
+ if (mi.isValid()) {
+ setData(mi, false, RoomFilterRole);
+ setData(mi, false, RoomAdmin);
+ setData(mi, false, Ready);
+ setData(mi, false, InGame);
+ updateIcon(mi);
+ }
+}
+
+void PlayersListModel::setFlag(const QString &nickname, StateFlag flagType,
+ bool isSet) {
+ if (flagType == Friend) {
+ if (isSet)
+ m_friendsSet.insert(nickname.toLower());
+ else
+ m_friendsSet.remove(nickname.toLower());
+
+ // FIXME: set proper file name
+ // saveSet(m_friendsSet, "friends");
+ } else if (flagType == Ignore) {
+ if (isSet)
+ m_ignoredSet.insert(nickname.toLower());
+ else
+ m_ignoredSet.remove(nickname.toLower());
+
+ // FIXME: set proper file name
+ // saveSet(m_ignoredSet, "ignore");
+ }
+
+ QModelIndex mi = nicknameIndex(nickname);
+
+ if (mi.isValid()) {
+ setData(mi, isSet, flagType);
+
+ if (flagType == Friend || flagType == ServerAdmin || flagType == Ignore ||
+ flagType == RoomAdmin)
+ updateSortData(mi);
+
+ updateIcon(mi);
+ }
+}
+
+bool PlayersListModel::isFlagSet(const QString &nickname, StateFlag flagType) {
+ QModelIndex mi = nicknameIndex(nickname);
+
+ if (mi.isValid())
+ return mi.data(flagType).toBool();
+ else if (flagType == Friend)
+ return isFriend(nickname);
+ else if (flagType == Ignore)
+ return isIgnored(nickname);
+ else
+ return false;
+}
+
+void PlayersListModel::resetRoomFlags() {
+ for (int i = rowCount() - 1; i >= 0; --i) {
+ QModelIndex mi = index(i);
+
+ if (mi.data(RoomFilterRole).toBool()) {
+ setData(mi, false, RoomFilterRole);
+ setData(mi, false, RoomAdmin);
+ setData(mi, false, Ready);
+ setData(mi, false, InGame);
+
+ updateSortData(mi);
+ updateIcon(mi);
+ }
+ }
+}
+
+void PlayersListModel::updateIcon(const QModelIndex &index) {
+ quint32 iconNum = 0;
+
+ QList<bool> flags;
+ flags << index.data(Ready).toBool() << index.data(ServerAdmin).toBool()
+ << index.data(RoomAdmin).toBool() << index.data(Registered).toBool()
+ << index.data(Friend).toBool() << index.data(Ignore).toBool()
+ << index.data(InGame).toBool() << index.data(RoomFilterRole).toBool()
+ << index.data(InRoom).toBool() << index.data(Contributor).toBool();
+
+ for (int i = flags.size() - 1; i >= 0; --i)
+ if (flags[i]) iconNum |= 1 << i;
+
+ if (m_icons().contains(iconNum)) {
+ setData(index, m_icons().value(iconNum), Qt::DecorationRole);
+ } else {
+ QPixmap result(24, 16);
+ result.fill(Qt::transparent);
+
+ QPainter painter(&result);
+
+ if (index.data(RoomFilterRole).toBool()) {
+ if (index.data(InGame).toBool()) {
+ painter.drawPixmap(0, 0, 16, 16, QPixmap(":/res/chat/ingame.png"));
+ } else {
+ if (index.data(Ready).toBool())
+ painter.drawPixmap(0, 0, 16, 16, QPixmap(":/res/chat/lamp.png"));
+ else
+ painter.drawPixmap(0, 0, 16, 16, QPixmap(":/res/chat/lamp_off.png"));
+ }
+ } else { // we're in lobby
+ if (!index.data(InRoom).toBool())
+ painter.drawPixmap(0, 0, 16, 16, QPixmap(":/res/Flake.png"));
+ }
+
+ QString mainIconName(":/res/chat/");
+
+ if (index.data(ServerAdmin).toBool())
+ mainIconName += "serveradmin";
+ else {
+ if (index.data(RoomAdmin).toBool())
+ mainIconName += "roomadmin";
+ else
+ mainIconName += "hedgehog";
+
+ if (index.data(Contributor).toBool()) mainIconName += "contributor";
+ }
+
+ if (!index.data(Registered).toBool()) mainIconName += "_gray";
+
+ painter.drawPixmap(8, 0, 16, 16, QPixmap(mainIconName + ".png"));
+
+ if (index.data(Ignore).toBool())
+ painter.drawPixmap(8, 0, 16, 16, QPixmap(":/res/chat/ignore.png"));
+ else if (index.data(Friend).toBool())
+ painter.drawPixmap(8, 0, 16, 16, QPixmap(":/res/chat/friend.png"));
+
+ painter.end();
+
+ QIcon icon(result);
+
+ setData(index, icon, Qt::DecorationRole);
+ m_icons().insert(iconNum, icon);
+ }
+
+ if (index.data(Ignore).toBool())
+ setData(index, QColor(Qt::gray), Qt::ForegroundRole);
+ else if (index.data(Friend).toBool())
+ setData(index, QColor(Qt::green), Qt::ForegroundRole);
+ else
+ setData(index, QBrush(QColor(0xff, 0xcc, 0x00)), Qt::ForegroundRole);
+}
+
+QHash<quint32, QIcon> &PlayersListModel::m_icons() {
+ static QHash<quint32, QIcon> iconsCache;
+
+ return iconsCache;
+}
+
+void PlayersListModel::updateSortData(const QModelIndex &index) {
+ QString result =
+ QString("%1%2%3%4%5%6")
+ // room admins go first, then server admins, then friends
+ .arg(1 - index.data(RoomAdmin).toInt())
+ .arg(1 - index.data(ServerAdmin).toInt())
+ .arg(1 - index.data(Friend).toInt())
+ // ignored at bottom
+ .arg(index.data(Ignore).toInt())
+ // keep nicknames starting from non-letter character at bottom within
+ // group assume there are no empty nicks in list
+ .arg(index.data(Qt::DisplayRole).toString().at(0).isLetter() ? 0 : 1)
+ // sort ignoring case
+ .arg(index.data(Qt::DisplayRole).toString().toLower());
+
+ setData(index, result, SortRole);
+}
+
+void PlayersListModel::setNickname(const QString &nickname) {
+ m_nickname = nickname;
+
+ // FIXME: set proper file names
+ // loadSet(m_friendsSet, "friends");
+ // loadSet(m_ignoredSet, "ignore");
+
+ for (int i = rowCount() - 1; i >= 0; --i) checkFriendIgnore(index(i));
+}
+
+bool PlayersListModel::isFriend(const QString &nickname) {
+ return m_friendsSet.contains(nickname.toLower());
+}
+
+bool PlayersListModel::isIgnored(const QString &nickname) {
+ return m_ignoredSet.contains(nickname.toLower());
+}
+
+void PlayersListModel::checkFriendIgnore(const QModelIndex &mi) {
+ setData(mi, isFriend(mi.data().toString()), Friend);
+ setData(mi, isIgnored(mi.data().toString()), Ignore);
+
+ updateIcon(mi);
+ updateSortData(mi);
+}
+
+void PlayersListModel::loadSet(QSet<QString> &set, const QString &fileName) {
+ set.clear();
+
+ QFile txt(fileName);
+ if (!txt.open(QIODevice::ReadOnly)) return;
+
+ QTextStream stream(&txt);
+ stream.setCodec("UTF-8");
+
+ while (!stream.atEnd()) {
+ QString str = stream.readLine();
+ if (str.startsWith(";") || str.isEmpty()) continue;
+
+ set.insert(str.trimmed());
+ }
+
+ txt.close();
+}
+
+void PlayersListModel::saveSet(const QSet<QString> &set,
+ const QString &fileName) {
+ qDebug("saving set");
+
+ QFile txt(fileName);
+
+ // list empty? => rather have no file for the list than an empty one
+ if (set.isEmpty()) {
+ if (txt.exists()) {
+ // try to remove file, if successful we're done here.
+ if (txt.remove()) return;
+ } else
+ // there is no file
+ return;
+ }
+
+ if (!txt.open(QIODevice::WriteOnly | QIODevice::Truncate)) return;
+
+ QTextStream stream(&txt);
+ stream.setCodec("UTF-8");
+
+ stream << "; this list is used by Hedgewars - do not edit it unless you know "
+ "what you're doing!"
+ << endl;
+
+ foreach (const QString &nick, set.values())
+ stream << nick << endl;
+
+ txt.close();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlfrontend/players_model.h Fri May 24 23:39:51 2019 +0200
@@ -0,0 +1,83 @@
+#ifndef PLAYERSLISTMODEL_H
+#define PLAYERSLISTMODEL_H
+
+#include <QAbstractListModel>
+#include <QFont>
+#include <QHash>
+#include <QIcon>
+#include <QModelIndex>
+#include <QSet>
+
+class PlayersListModel : public QAbstractListModel {
+ Q_OBJECT
+
+ public:
+ enum StateFlag {
+ Ready = Qt::UserRole,
+ ServerAdmin = Qt::UserRole + 1,
+ RoomAdmin = Qt::UserRole + 2,
+ Registered = Qt::UserRole + 3,
+ Friend = Qt::UserRole + 4,
+ Ignore = Qt::UserRole + 5,
+ InGame = Qt::UserRole + 6,
+ InRoom = Qt::UserRole + 7,
+ Contributor = Qt::UserRole + 8
+ // if you add a role that will affect the player icon,
+ // then also add it to the flags Qlist in updateIcon()!
+ };
+
+ enum SpecialRoles {
+ SortRole = Qt::UserRole + 100,
+ RoomFilterRole = Qt::UserRole + 101
+ };
+
+ explicit PlayersListModel(QObject *parent = 0);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ QVariant data(const QModelIndex &index, int role) const override;
+ bool setData(const QModelIndex &index, const QVariant &value,
+ int role = Qt::DisplayRole) override;
+
+ void setFlag(const QString &nickname, StateFlag flagType, bool isSet);
+ bool isFlagSet(const QString &nickname, StateFlag flagType);
+
+ bool insertRows(int row, int count,
+ const QModelIndex &parent = QModelIndex()) override;
+ bool removeRows(int row, int count,
+ const QModelIndex &parent = QModelIndex()) override;
+
+ QModelIndex nicknameIndex(const QString &nickname);
+
+ public slots:
+ void addPlayer(const QString &nickname, bool notify);
+ void removePlayer(const QString &nickname, const QString &msg = QString());
+ void playerJoinedRoom(const QString &nickname, bool notify);
+ void playerLeftRoom(const QString &nickname);
+ void resetRoomFlags();
+ void setNickname(const QString &nickname);
+
+ signals:
+ void nickAdded(const QString &nick, bool notifyNick);
+ void nickRemoved(const QString &nick);
+ void nickAddedLobby(const QString &nick, bool notifyNick);
+ void nickRemovedLobby(const QString &nick, const QString &message);
+
+ private:
+ QHash<quint32, QIcon> &m_icons();
+ using DataEntry = QHash<int, QVariant>;
+ QList<DataEntry> m_data;
+ QSet<QString> m_friendsSet, m_ignoredSet;
+ QString m_nickname;
+ QFont m_fontInRoom;
+
+ void updateIcon(const QModelIndex &index);
+ void updateSortData(const QModelIndex &index);
+ void loadSet(QSet<QString> &set, const QString &fileName);
+ void saveSet(const QSet<QString> &set, const QString &fileName);
+ void checkFriendIgnore(const QModelIndex &mi);
+ bool isFriend(const QString &nickname);
+ bool isIgnored(const QString &nickname);
+};
+
+#endif // PLAYERSLISTMODEL_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlfrontend/rooms_model.cpp Fri May 24 23:39:51 2019 +0200
@@ -0,0 +1,231 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * @brief RoomsListModel class implementation
+ */
+
+#include <QBrush>
+#include <QColor>
+#include <QIcon>
+
+#include "rooms_model.h"
+
+RoomsListModel::RoomsListModel(QObject *parent)
+ : QAbstractTableModel(parent), c_nColumns(9) {
+ m_headerData = QStringList();
+ m_headerData << tr("In progress");
+ m_headerData << tr("Room Name");
+ //: Caption of the column for the number of connected clients in the list of
+ // rooms
+ m_headerData << tr("C");
+ //: Caption of the column for the number of teams in the list of rooms
+ m_headerData << tr("T");
+ m_headerData << tr("Owner");
+ m_headerData << tr("Map");
+ m_headerData << tr("Script");
+ m_headerData << tr("Rules");
+ m_headerData << tr("Weapons");
+
+ // m_staticMapModel = DataManager::instance().staticMapModel();
+ // m_missionMapModel = DataManager::instance().missionMapModel();
+}
+
+QVariant RoomsListModel::headerData(int section, Qt::Orientation orientation,
+ int role) const {
+ if (orientation == Qt::Vertical || role != Qt::DisplayRole)
+ return QVariant();
+ else
+ return QVariant(m_headerData.at(section));
+}
+
+int RoomsListModel::rowCount(const QModelIndex &parent) const {
+ if (parent.isValid())
+ return 0;
+ else
+ return m_data.size();
+}
+
+int RoomsListModel::columnCount(const QModelIndex &parent) const {
+ if (parent.isValid())
+ return 0;
+ else
+ return c_nColumns;
+}
+
+QVariant RoomsListModel::data(const QModelIndex &index, int role) const {
+ int column = index.column();
+ int row = index.row();
+
+ // invalid index
+ if (!index.isValid()) return QVariant();
+
+ // invalid row
+ if ((row < 0) || (row >= m_data.size())) return QVariant();
+
+ // invalid column
+ if ((column < 0) || (column >= c_nColumns)) return QVariant();
+
+ // not a role we have data for
+ if (role != Qt::DisplayRole)
+ // only custom-align counters
+ if ((role != Qt::TextAlignmentRole) ||
+ ((column != PlayerCountColumn) && (column != TeamCountColumn)))
+ // only decorate name column
+ if ((role != Qt::DecorationRole) || (column != NameColumn))
+ // only dye map column
+ if ((role != Qt::ForegroundRole) || (column != MapColumn))
+ return QVariant();
+
+ // decorate room name based on room state
+ if (role == Qt::DecorationRole) {
+ const QIcon roomBusyIcon(":/res/iconDamage.png");
+ const QIcon roomBusyIconGreen(":/res/iconDamageLockG.png");
+ const QIcon roomBusyIconRed(":/res/iconDamageLockR.png");
+ const QIcon roomWaitingIcon(":/res/iconTime.png");
+ const QIcon roomWaitingIconGreen(":/res/iconTimeLockG.png");
+ const QIcon roomWaitingIconRed(":/res/iconTimeLockR.png");
+
+ QString flags = m_data.at(row).at(StateColumn);
+
+ if (flags.contains("g")) {
+ if (flags.contains("j"))
+ return QVariant(roomBusyIconRed);
+ else if (flags.contains("p"))
+ return QVariant(roomBusyIconGreen);
+ else
+ return QVariant(roomBusyIcon);
+ } else {
+ if (flags.contains("j"))
+ return QVariant(roomWaitingIconRed);
+ else if (flags.contains("p"))
+ return QVariant(roomWaitingIconGreen);
+ else
+ return QVariant(roomWaitingIcon);
+ }
+ }
+
+ QString content = m_data.at(row).at(column);
+
+ if (role == Qt::DisplayRole) {
+ // display room names
+ if (column == 5) {
+ // special names
+ if (content[0] == '+') {
+ if (content == "+rnd+") return tr("Random Map");
+ if (content == "+maze+") return tr("Random Maze");
+ if (content == "+perlin+") return tr("Random Perlin");
+ if (content == "+drawn+") return tr("Hand-drawn");
+ if (content == "+forts+") return tr("Forts");
+ }
+
+ // prefix ? if map not available
+ /*if (!m_staticMapModel->mapExists(content) &&
+ !m_missionMapModel->mapExists(content))
+ return QString("? %1").arg(content);*/
+ }
+
+ return content;
+ }
+
+ // dye map names red if map not available
+ /*if (role == Qt::ForegroundRole) {
+ if (content == "+rnd+" || content == "+maze+" || content == "+perlin+" ||
+ content == "+drawn+" || content == "+forts+" ||
+ m_staticMapModel->mapExists(content) ||
+ m_missionMapModel->mapExists(content))
+ return QVariant();
+ else
+ return QBrush(QColor("darkred"));
+ }*/
+
+ if (role == Qt::TextAlignmentRole) {
+ return (int)(Qt::AlignHCenter | Qt::AlignVCenter);
+ }
+
+ Q_ASSERT(false);
+ return QVariant();
+}
+
+void RoomsListModel::setRoomsList(const QStringList &rooms) {
+ beginResetModel();
+
+ m_data.clear();
+
+ int nRooms = rooms.size();
+
+ for (int i = 0; i < nRooms; i += c_nColumns) {
+ QStringList l;
+ l.reserve(c_nColumns);
+
+ for (int t = 0; t < c_nColumns; t++) {
+ l.append(rooms[i + t]);
+ }
+
+ m_data.append(l);
+ }
+
+ endResetModel();
+}
+
+void RoomsListModel::addRoom(const QStringList &info) {
+ beginInsertRows(QModelIndex(), 0, 0);
+
+ m_data.prepend(info);
+
+ endInsertRows();
+}
+
+int RoomsListModel::rowOfRoom(const QString &name) {
+ int size = m_data.size();
+
+ if (size < 1) return -1;
+
+ int i = 0;
+
+ // search for record with matching room name
+ while (m_data[i].at(NameColumn) != name) {
+ i++;
+ if (i >= size) return -1;
+ }
+
+ return i;
+}
+
+void RoomsListModel::removeRoom(const QString &name) {
+ int i = rowOfRoom(name);
+
+ if (i < 0) return;
+
+ beginRemoveRows(QModelIndex(), i, i);
+
+ m_data.removeAt(i);
+
+ endRemoveRows();
+}
+
+void RoomsListModel::updateRoom(const QString &name, const QStringList &info) {
+ int i = rowOfRoom(name);
+
+ if (i < 0) return;
+
+ m_data[i] = info;
+
+ emit dataChanged(index(i, 0), index(i, columnCount(QModelIndex()) - 1));
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlfrontend/rooms_model.h Fri May 24 23:39:51 2019 +0200
@@ -0,0 +1,69 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * @brief RoomsListModel class definition
+ */
+
+#ifndef HEDGEWARS_ROOMSLISTMODEL_H
+#define HEDGEWARS_ROOMSLISTMODEL_H
+
+#include <QAbstractTableModel>
+#include <QStringList>
+
+class RoomsListModel : public QAbstractTableModel {
+ Q_OBJECT
+ public:
+ // if you add a column here, also incr. c_nColumns in constructor
+ // also adjust header in constructor to changes
+ enum Column {
+ StateColumn,
+ NameColumn,
+ PlayerCountColumn,
+ TeamCountColumn,
+ OwnerColumn,
+ MapColumn,
+ SchemeColumn,
+ WeaponsColumn
+ };
+
+ explicit RoomsListModel(QObject *parent = 0);
+
+ QVariant headerData(int section, Qt::Orientation orientation,
+ int role) const override;
+ int rowCount(const QModelIndex &parent) const override;
+ int columnCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+
+ public slots:
+ void setRoomsList(const QStringList &rooms);
+ void addRoom(const QStringList &info);
+ void removeRoom(const QString &name);
+ void updateRoom(const QString &name, const QStringList &info);
+ int rowOfRoom(const QString &name);
+
+ private:
+ const int c_nColumns;
+ QList<QStringList> m_data;
+ QStringList m_headerData;
+ // MapModel * m_staticMapModel;
+ // MapModel * m_missionMapModel;
+};
+
+#endif // HEDGEWARS_ROOMSLISTMODEL_H