diff --git a/ACLEditor.cpp b/ACLEditor.cpp
index c48de8ba4..8e03c46f6 100644
--- a/ACLEditor.cpp
+++ b/ACLEditor.cpp
@@ -353,7 +353,7 @@ void ACLEditor::addToolTipsWhatsThis() {
"even if he doesn't belong to the admin group in the channel where the ACL originated.
"
"A few special predefined groups are:
"
"all - Everyone will match.
"
- "reg - All registered users will match.
"
+ "auth - All authenticated users will match.
"
"in - Users currently in the channel will match.
"
"out - Users outside the channel will match.
"
"Note that an entry applies to either a user or a group, not both."));
@@ -739,7 +739,7 @@ void ACLEditor::ACLEnableCheck() {
qcbACLGroup->clear();
qcbACLGroup->addItem(QString());
qcbACLGroup->addItem("all");
- qcbACLGroup->addItem("reg");
+ qcbACLGroup->addItem("auth");
qcbACLGroup->addItem("in");
qcbACLGroup->addItem("out");
foreach(gs, groups)
diff --git a/ConfigDialog.cpp b/ConfigDialog.cpp
index 05495b726..a0f647014 100644
--- a/ConfigDialog.cpp
+++ b/ConfigDialog.cpp
@@ -89,10 +89,6 @@ ConfigDialog::ConfigDialog(QWidget *p) : QDialog(p) {
addPage(new LogConfig());
addPage(new PluginConfig());
- QHBoxLayout *top = new QHBoxLayout;
- top->addWidget(qlwIcons);
- top->addWidget(qswPages, 1);
-
QHBoxLayout *buttons = new QHBoxLayout;
buttons->addStretch(1);
buttons->addWidget(applyButton);
@@ -100,15 +96,19 @@ ConfigDialog::ConfigDialog(QWidget *p) : QDialog(p) {
buttons->addWidget(cancelButton);
QVBoxLayout *l = new QVBoxLayout;
- l->addLayout(top);
- l->addStretch(1);
+ l->addWidget(qswPages, 1);
l->addSpacing(12);
l->addLayout(buttons);
- setLayout(l);
+
+
+ QHBoxLayout *top = new QHBoxLayout;
+ top->addWidget(qlwIcons);
+ top->addLayout(l);
+
+ setLayout(top);
qlwIcons->scrollTo(qlwIcons->currentIndex(), QAbstractItemView::PositionAtTop);
-
QMetaObject::connectSlotsByName(this);
qlwIcons->setCurrentRow(0);
diff --git a/DXAudioOutput.cpp b/DXAudioOutput.cpp
index 042a7eb3f..74129a62d 100644
--- a/DXAudioOutput.cpp
+++ b/DXAudioOutput.cpp
@@ -167,7 +167,7 @@ bool DXAudioOutputPlayer::playFrames() {
dwLastPlayPos = 0;
dwTotalPlayPos = 0;
- iLastwriteblock = NBLOCKS - 1;
+ iLastwriteblock = (NBLOCKS - 1 + g.s.iDXOutputDelay) % NBLOCKS;
bPlaying = true;
}
diff --git a/Group.cpp b/Group.cpp
index 30808aa2c..462dc26af 100644
--- a/Group.cpp
+++ b/Group.cpp
@@ -128,7 +128,7 @@ bool Group::isMember(Channel *c, QString name, int id) {
if (name == "all")
return true;
- if (name == "reg")
+ if (name == "auth")
return (id >= 0);
if (name == "in") {
diff --git a/INSTALL b/INSTALL
index 84d750066..0a6f0cd4c 100644
--- a/INSTALL
+++ b/INSTALL
@@ -51,5 +51,9 @@ To build murmur, type
qmake murmur.pro
make
-Note that you need to set up and configure murmur.pl as a CGI on your
-webserver for user registration to work.
+To enable user registration, copy murmur.pl to murmur.cgi and put it
+somewhere accessible by your webserver. Make sure your webserver is
+configured to execute it as a CGI, and make sure you edit it to reflect
+the configuration parameters. Note that the user the CGI will run as needs
+write permissions to both the database file and the directory the database
+file resides in.
diff --git a/MainWindow.cpp b/MainWindow.cpp
index 9a7066adb..69d7c7533 100644
--- a/MainWindow.cpp
+++ b/MainWindow.cpp
@@ -175,20 +175,20 @@ void MainWindow::createActions() {
}
void MainWindow::setupGui() {
- QMenu *qmServer, *qmPlayer, *qmChannel, *qmAudio, *qmConfig, *qmHelp;
-
setWindowTitle(tr("Mumble -- %1").arg(QString(MUMBLE_RELEASE)));
+ pmModel = new PlayerModel(this);
+
QTreeView *view = new QTreeView(this);
qtvPlayers = view;
-
- pmModel = new PlayerModel(this);
- view->setModel(pmModel);
- view->setItemDelegate(new PlayerDelegate(view));
- view->setDragEnabled(true);
- view->setDropIndicatorShown(true);
- view->setAcceptDrops(true);
- view->setIndentation(10);
+ qtvPlayers->setObjectName("Players");
+ qtvPlayers->setContextMenuPolicy(Qt::CustomContextMenu);
+ qtvPlayers->setModel(pmModel);
+ qtvPlayers->setItemDelegate(new PlayerDelegate(view));
+ qtvPlayers->setDragEnabled(true);
+ qtvPlayers->setAcceptDrops(true);
+ qtvPlayers->setDropIndicatorShown(true);
+ qtvPlayers->setIndentation(10);
qteLog = new QTextEdit(this);
qteLog->setReadOnly(true);
@@ -307,6 +307,20 @@ void MainWindow::appendLog(QString entry)
qteLog->ensureCursorVisible();
}
+void MainWindow::on_Players_customContextMenuRequested(const QPoint &mpos) {
+ QModelIndex idx = qtvPlayers->indexAt(mpos);
+ if (! idx.isValid())
+ idx = qtvPlayers->currentIndex();
+ Player *p = pmModel->getPlayer(idx);
+
+ if (p) {
+ qmPlayer->popup(qtvPlayers->mapToGlobal(mpos), qaPlayerMute);
+ } else {
+ qmChannel->popup(qtvPlayers->mapToGlobal(mpos), qaChannelACL);
+ }
+}
+
+
void MainWindow::on_ServerConnect_triggered()
{
ConnectDialog *cd = new ConnectDialog(this);
@@ -384,11 +398,12 @@ void MainWindow::on_PlayerKick_triggered()
void MainWindow::on_ChannelMenu_aboutToShow()
{
- Channel *c = pmModel->getChannel(qtvPlayers->currentIndex());
+ QModelIndex idx = qtvPlayers->currentIndex();
+ Channel *c = pmModel->getChannel(idx);
if (! c) {
- qaChannelAdd->setEnabled(false);
+ qaChannelAdd->setEnabled(g.sId != 0);
qaChannelRemove->setEnabled(false);
- qaChannelACL->setEnabled(false);
+ qaChannelACL->setEnabled(g.sId != 0);
} else {
qaChannelAdd->setEnabled(true);
qaChannelRemove->setEnabled(true);
@@ -400,13 +415,12 @@ void MainWindow::on_ChannelAdd_triggered()
{
bool ok;
Channel *c = pmModel->getChannel(qtvPlayers->currentIndex());
- if (! c)
- return;
+ int iParent = c ? c->iId : 0;
QString name = QInputDialog::getText(this, tr("Mumble"), tr("Channel Name"), QLineEdit::Normal, "", &ok);
if (ok) {
MessageChannelAdd mca;
mca.qsName = name;
- mca.iParent = c->iId;
+ mca.iParent = iParent;
g.sh->sendMessage(&mca);
}
}
@@ -430,11 +444,10 @@ void MainWindow::on_ChannelRemove_triggered()
void MainWindow::on_ChannelACL_triggered()
{
Channel *c = pmModel->getChannel(qtvPlayers->currentIndex());
- if (! c)
- return;
+ int id = c ? c->iId : 0;
MessageEditACL mea;
- mea.iId = c->iId;
+ mea.iId = id;
mea.bQuery = true;
g.sh->sendMessage(&mea);
diff --git a/MainWindow.h b/MainWindow.h
index 6dc128518..9c33b90ae 100644
--- a/MainWindow.h
+++ b/MainWindow.h
@@ -37,7 +37,8 @@
#include
#include
#include
-#include
+#include
+#include
#include "Audio.h"
#include "TrayIcon.h"
#include "ACLEditor.h"
@@ -50,9 +51,11 @@ class TextToSpeech;
class PlayerModel;
class MainWindow : public QMainWindow {
+ friend class PlayerModel;
Q_OBJECT
public:
PlayerModel *pmModel;
+ QMenu *qmServer, *qmPlayer, *qmChannel, *qmAudio, *qmConfig, *qmHelp;
QAction *qaServerConnect, *qaServerDisconnect;
QAction *qaPlayerKick, *qaPlayerMute, *qaPlayerDeaf;
QAction *qaAudioReset, *qaAudioMute, *qaAudioDeaf, *qaAudioTTS, *qaAudioStats, *qaAudioUnlink;
@@ -72,12 +75,13 @@ class MainWindow : public QMainWindow {
void appendLog(QString entry);
protected:
QTextEdit *qteLog;
- QAbstractItemView *qtvPlayers;
+ QTreeView *qtvPlayers;
void createActions();
void setupGui();
void customEvent(QEvent *evt);
virtual void closeEvent(QCloseEvent *e);
public slots:
+ void on_Players_customContextMenuRequested(const QPoint &pos);
void on_ServerConnect_triggered();
void on_ServerDisconnect_triggered();
void on_PlayerMenu_aboutToShow();
diff --git a/PlayerModel.cpp b/PlayerModel.cpp
index 816d133d7..3eb67de5e 100644
--- a/PlayerModel.cpp
+++ b/PlayerModel.cpp
@@ -30,29 +30,153 @@
#include
#include
+#include
+#include
+#include
#include "PlayerModel.h"
#include "MainWindow.h"
#include "Message.h"
#include "ServerHandler.h"
#include "Global.h"
-ChannelItem::ChannelItem(ChannelItem *p, Channel *cptr) {
- ciParent = p;
- c = cptr;
+QHash ModelItem::c_qhChannels;
+QHash ModelItem::c_qhPlayers;
+
+ModelItem::ModelItem(Channel *c) {
+ this->cChan = c;
+ this->pPlayer = NULL;
+ c_qhChannels[c] = this;
}
-void ChannelItem::dump() {
- Channel *subc;
- qWarning("ChannelItem %p", this);
- qWarning("Parent %p", ciParent);
- qWarning("Channel %p (%s)", c, qPrintable(c->qsName));
- qWarning("Channels: %d", qlChannels.count());
- qWarning("Players: %d", qlPlayers.count());
- foreach (subc, qlChannels)
- qWarning("SubChannel %d", subc->iId);
- qWarning("--");
- foreach (subc, qlChannels)
- g.mw->pmModel->qhChannelItems[subc]->dump();
+ModelItem::ModelItem(Player *p) {
+ this->cChan = NULL;
+ this->pPlayer = p;
+ c_qhPlayers[p] = this;
+}
+
+ModelItem::~ModelItem() {
+ Q_ASSERT(qlPlayers.count() == 0);
+ Q_ASSERT(qlChannels.count() == 0);
+
+ if (cChan)
+ c_qhChannels.remove(cChan);
+ if (pPlayer)
+ c_qhPlayers.remove(pPlayer);
+}
+
+ModelItem *ModelItem::parent() const {
+ Channel *p;
+
+ if (cChan)
+ p = cChan->cParent;
+ else
+ p = pPlayer->cChannel;
+
+ return c_qhChannels.value(p);
+}
+
+ModelItem *ModelItem::child(int idx) const {
+ if (! validRow(idx))
+ return NULL;
+
+ if (idx < qlChannels.count())
+ return c_qhChannels.value(channelAt(idx));
+ else
+ return c_qhPlayers.value(playerAt(idx));
+}
+
+bool ModelItem::validRow(int idx) const {
+ return ((idx >= 0) && (idx < (qlPlayers.count() + qlChannels.count())));
+}
+
+Player *ModelItem::playerAt(int idx) const {
+ idx -= qlChannels.count();
+ if ((idx>= 0) && (idx < qlPlayers.count()))
+ return qlPlayers.at(idx);
+ return NULL;
+}
+
+Channel *ModelItem::channelAt(int idx) const {
+ if ((idx>= 0) && (idx < qlChannels.count()))
+ return qlChannels.at(idx);
+ return NULL;
+}
+
+int ModelItem::rowOf(Channel *c) const {
+ return qlChannels.lastIndexOf(c);
+}
+
+int ModelItem::rowOf(Player *p) const {
+ int v = qlPlayers.lastIndexOf(p);
+ if (v != -1)
+ v += qlChannels.count();
+ return v;
+}
+
+int ModelItem::rowOfSelf() const {
+ ModelItem *p = parent();
+
+ Q_ASSERT(p);
+
+ if (pPlayer)
+ return p->rowOf(pPlayer);
+ else
+ return p->rowOf(cChan);
+}
+
+int ModelItem::rows() const {
+ return qlPlayers.count() + qlChannels.count();
+}
+
+int ModelItem::insertIndex(Channel *c) const {
+ QList qls;
+ Channel *cp;
+
+ foreach(cp, qlChannels)
+ qls << cp->qsName;
+ qls << c->qsName;
+ qSort(qls);
+
+ return qls.lastIndexOf(c->qsName);
+}
+
+int ModelItem::insertIndex(Player *p) const {
+ QList qls;
+ Player *pp;
+
+ foreach(pp, qlPlayers)
+ qls << pp->qsName;
+ qls << p->qsName;
+ qSort(qls);
+
+ return qls.lastIndexOf(p->qsName);
+}
+
+void ModelItem::insertChannel(Channel *c) {
+ int idx = insertIndex(c);
+ qlChannels.insert(idx, c);
+}
+
+void ModelItem::insertPlayer(Player *p) {
+ int idx = insertIndex(p);
+ qlPlayers.insert(idx, p);
+}
+
+bool ModelItem::isValid() const {
+ if (pPlayer && cChan)
+ qFatal("ModelItem: Both Player and Channel");
+ if (!pPlayer && !cChan)
+ qFatal("ModelItem: Neither Player nor Channel");
+
+ if (pPlayer)
+ return true;
+
+ if (cChan->qlChannels.toSet() != qlChannels.toSet())
+ qFatal("ModelItem: Channel mismatch");
+ if (cChan->qlPlayers.toSet() != qlPlayers.toSet())
+ qFatal("ModelItem: Player mistmatch");
+
+ return true;
}
PlayerModel::PlayerModel(QObject *p) : QAbstractItemModel(p) {
@@ -63,14 +187,15 @@ PlayerModel::PlayerModel(QObject *p) : QAbstractItemModel(p) {
qiDeafenedSelf=QIcon(":/icons/deafened_self.png");
qiDeafenedServer=QIcon(":/icons/deafened_server.png");
qiAuthenticated=QIcon(":/icons/authenticated.png");
+ qiChannel=QIcon(":/icons/channel.png");
- Channel *c = Channel::get(0);
- ciRoot = new ChannelItem(NULL, c);
- qhChannelItems[c] = ciRoot;
+ miRoot = new ModelItem(Channel::get(0));
}
PlayerModel::~PlayerModel() {
removeAll();
+ Q_ASSERT(ModelItem::c_qhPlayers.count() == 0);
+ Q_ASSERT(ModelItem::c_qhChannels.count() == 1);
}
@@ -81,103 +206,102 @@ int PlayerModel::columnCount(const QModelIndex &) const
QModelIndex PlayerModel::index(int row, int column, const QModelIndex &p) const
{
- ChannelItem *item;
+ ModelItem *item;
QModelIndex idx = QModelIndex();
+ qDebug("index(%d,%d,%s)",row,column,qPrintable(stringIndex(p)));
+
if (row == -1) {
return QModelIndex();
}
if ( ! p.isValid()) {
- item = ciRoot;
+ item = miRoot;
} else {
- item = static_cast(p.internalPointer());
- if (p.row() < 0 || p.row() >= item->qlChannels.count()) {
- return idx;
- }
- item = qhChannelItems.value(item->qlChannels[p.row()]);
+ item = static_cast(p.internalPointer());
}
- if (row >= (item->qlPlayers.count() + item->qlChannels.count()))
+
+ item->isValid();
+
+ if (! item->validRow(row))
return idx;
- idx = createIndex(row, column, item);
+ Q_ASSERT(item->child(row));
+ idx = createIndex(row, column, item->child(row));
+
+ qDebug(" => %s",qPrintable(stringIndex(idx)));
return idx;
}
QModelIndex PlayerModel::index(Player *p, int column) const
{
- ChannelItem *item = qhChannelItems.value(p->cChannel);
- QModelIndex idx=createIndex(item->qlChannels.count() + item->qlPlayers.indexOf(p), column, item);
+ ModelItem *item = ModelItem::c_qhPlayers.value(p);
+ Q_ASSERT(p);
+ Q_ASSERT(item);
+ QModelIndex idx=createIndex(item->rowOfSelf(), 0, item);
return idx;
}
QModelIndex PlayerModel::index(Channel *c) const
{
- ChannelItem *item = qhChannelItems.value(c->cParent);
- if (! item)
+ ModelItem *item = ModelItem::c_qhChannels.value(c);
+ Q_ASSERT(c);
+ Q_ASSERT(item);
+ if (!c || ! c->parent())
return QModelIndex();
-
- QModelIndex idx=createIndex(item->qlChannels.indexOf(c), 0, item);
-
+ QModelIndex idx=createIndex(item->rowOfSelf(), 0, item);
return idx;
}
-QModelIndex PlayerModel::index(ChannelItem *ci) const
-{
- return index(ci->c);
-}
-
QModelIndex PlayerModel::parent(const QModelIndex &idx) const
{
+ qDebug("parent(%s)",qPrintable(stringIndex(idx)));
if (! idx.isValid())
return QModelIndex();
- ChannelItem *item = static_cast(idx.internalPointer());
+ ModelItem *item = static_cast(idx.internalPointer());
+ ModelItem *pitem = item->parent();
+ ModelItem *gpitem = (pitem) ? pitem->parent() : NULL;
- if (! item || ! item->ciParent)
- return QModelIndex();
+ if (! pitem || ! gpitem)
+ return QModelIndex();
- if (idx.row() >= (item->qlPlayers.count() + item->qlChannels.count()))
- return QModelIndex();
+ QModelIndex pidx = createIndex(pitem->rowOfSelf(), 0, pitem);
- QModelIndex pidx = createIndex(item->ciParent->qlChannels.indexOf(item->c), 0, item->ciParent);
+ qDebug(" => %s",qPrintable(stringIndex(pidx)));
return pidx;
}
-
int PlayerModel::rowCount(const QModelIndex &p) const
{
- ChannelItem *item;
+ ModelItem *item;
+
int val = 0;
if (!p.isValid())
- item = ciRoot;
+ item = miRoot;
else
- item = static_cast(p.internalPointer());
+ item = static_cast(p.internalPointer());
- if (p.row() == -1) {
- // Catch when it's asking for "this" item
- val = item->qlPlayers.count() + item->qlChannels.count();
- } else if (p.row() >= item->qlChannels.count()) {
- val = 0;
- } else {
- item = qhChannelItems.value(item->qlChannels[p.row()]);
- val = item->qlPlayers.count() + item->qlChannels.count();
- }
+ val = item->rows();
+ qDebug("rowcount(%s) => %d",qPrintable(stringIndex(p)),val);
return val;
}
QString PlayerModel::stringIndex(const QModelIndex &idx) const
{
- ChannelItem *item = static_cast(idx.internalPointer());
+ ModelItem *item = static_cast(idx.internalPointer());
if (!idx.isValid())
return QString("invIdx");
if (!item)
return QString("invPtr");
- return QString("[%1(%2,%3 %4 %5)]").arg(item->c->qsName).arg(idx.row()).arg(idx.column()).arg(item->qlChannels.count()).arg(item->qlPlayers.count());
+ if (item->pPlayer)
+ return QString("P:%1 [%2,%3]").arg(item->pPlayer->qsName).arg(idx.row()).arg(idx.column());
+ else
+ return QString("C:%1 [%2,%3]").arg(item->cChan->qsName).arg(idx.row()).arg(idx.column());
}
QVariant PlayerModel::data(const QModelIndex &idx, int role) const
@@ -185,55 +309,62 @@ QVariant PlayerModel::data(const QModelIndex &idx, int role) const
if (!idx.isValid())
return QVariant();
- QVariant v = otherRoles(idx.column(), role);
+ ModelItem *item = static_cast(idx.internalPointer());
+ item->isValid();
+
+ Channel *c = item->cChan;
+ Player *p = item->pPlayer;
+
+ if (!c && !p)
+ return QVariant();
+
+ QVariant v = otherRoles(idx.column(), role, (p != NULL));
if (v.isValid())
return v;
- int row = idx.row();
+ QList l;
- ChannelItem *item = static_cast(idx.internalPointer());
-
- if (row < item->qlChannels.count()) {
- if (role != Qt::DisplayRole)
- return QVariant();
- return item->qlChannels[row]->qsName;
+ if (p) {
+ switch (role) {
+ case Qt::DecorationRole:
+ if (idx.column() == 0)
+ return (p->bTalking) ? qiTalkingOn : qiTalkingOff;
+ break;
+ case Qt::FontRole:
+ if ((idx.column() == 0) && (p->sId == g.sId)) {
+ QFont f = g.mw->font();
+ f.setBold(true);
+ return f;
+ }
+ break;
+ case Qt::DisplayRole:
+ if (idx.column() == 0)
+ return p->qsName;
+ if (p->iId >= 0)
+ l << qiAuthenticated;
+ if (p->bMute)
+ l << qiMutedServer;
+ if (p->bDeaf)
+ l << qiDeafenedServer;
+ if (p->bSelfMute)
+ l << qiMutedSelf;
+ if (p->bSelfDeaf)
+ l << qiDeafenedSelf;
+ return l;
+ default:
+ break;
+ }
} else {
- row -= item->qlChannels.count();
- }
-
- if (row >= item->qlPlayers.count())
- return QVariant();
-
- Player *p = item->qlPlayers[row];
-
- if ((role == Qt::DecorationRole) && (idx.column() == 0))
- return (p->bTalking) ? qiTalkingOn : qiTalkingOff;
-
- if ((role == Qt::FontRole) && (idx.column() == 0) && (p->sId == g.sId)) {
- QFont f = g.mw->font();
- f.setBold(true);
- return f;
- }
-
- if (role != Qt::DisplayRole)
- return QVariant();
-
- switch(idx.column()) {
- case 0:
- return p->qsName;
- case 1:
- QList l;
- if (p->iId >= 0)
- l << qiAuthenticated;
- if (p->bMute)
- l << qiMutedServer;
- if (p->bDeaf)
- l << qiDeafenedServer;
- if (p->bSelfMute)
- l << qiMutedSelf;
- if (p->bSelfDeaf)
- l << qiDeafenedSelf;
- return l;
+ switch (role) {
+ case Qt::DecorationRole:
+ if (idx.column() == 0)
+ return qiChannel;
+ case Qt::DisplayRole:
+ if (idx.column() == 0)
+ return c->qsName;
+ default:
+ break;
+ }
}
return QVariant();
}
@@ -249,25 +380,28 @@ Qt::ItemFlags PlayerModel::flags(const QModelIndex &idx) const
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
}
-QVariant PlayerModel::otherRoles(int section, int role) const
+QVariant PlayerModel::otherRoles(int section, int role, bool isPlayer) const
{
switch(role) {
case Qt::ToolTipRole:
switch(section) {
case 0:
- return tr("Name of player");
+ return isPlayer ? tr("Name of player") : tr("Name of channel");
case 1:
- return tr("Player flags");
+ return isPlayer ? tr("Player flags") : QVariant();
}
break;
case Qt::WhatsThisRole:
switch(section) {
case 0:
- return tr("This list shows all the players connected to the server. The icon to the left of the player indicates "
- "whether or not they are talking:
"
- "
Talking
"
- "
Not talking"
- );
+ if (isPlayer)
+ return tr("This is a player connected to the server. The icon to the left of the player indicates "
+ "whether or not they are talking:
"
+ "
Talking
"
+ "
Not talking"
+ );
+ else
+ return tr("This is a channel on the server. Only players in the same channel can hear each other.");
case 1:
return tr("This shows the flags the player has on the server, if any:
"
"
Authenticated user
"
@@ -290,11 +424,6 @@ QVariant PlayerModel::headerData(int section, Qt::Orientation orientation,
if (orientation != Qt::Horizontal)
return QVariant();
- QVariant v = otherRoles(section, role);
-
- if (v.isValid())
- return v;
-
switch(role) {
case Qt::DisplayRole:
switch(section) {
@@ -308,44 +437,74 @@ QVariant PlayerModel::headerData(int section, Qt::Orientation orientation,
return QVariant();
}
+void PlayerModel::unbugHide(const QModelIndex &idx) {
+ QAbstractItemView *v=g.mw->qtvPlayers;
+ QItemSelectionModel *sel=v->selectionModel();
+
+ // If we hide the current item and re-show it as a child
+ // an item that is nonexpanded, and then expand
+ // that item, we get "interresting results".
+ //
+ // A permanent fix for this would be to let the private
+ // pointer of the index be a struct with channel/player,
+ // and recalculate the correct (sorted) position for each and
+ // every use.
+
+ if (sel->isSelected(idx) || (idx == v->currentIndex())) {
+ v->clearSelection();
+ v->setCurrentIndex(QModelIndex());
+ }
+}
+
void PlayerModel::hidePlayer(Player *p) {
Channel *c = p->cChannel;
- ChannelItem *item = qhChannelItems.value(c);
+ ModelItem *item = ModelItem::c_qhChannels.value(c);
- int row = item->qlPlayers.indexOf(p);
- int rowidx = row + item->qlChannels.count();
+ int row = item->rowOf(p);
- beginRemoveRows(index(item), rowidx, rowidx);
+ unbugHide(index(p));
+
+ beginRemoveRows(index(c), row, row);
c->removePlayer(p);
- item->qlPlayers.removeAt(row);
+ item->qlPlayers.removeAll(p);
endRemoveRows();
p->cChannel = NULL;
}
void PlayerModel::showPlayer(Player *p, Channel *c) {
- ChannelItem *item = qhChannelItems.value(c);
+ ModelItem *item = ModelItem::c_qhChannels.value(c);
- QStringList names;
- foreach(Player *chanp, item->qlPlayers) {
- names << chanp->qsName;
- }
- names << p->qsName;
- qSort(names);
+ Q_ASSERT(p);
+ Q_ASSERT(c);
+ Q_ASSERT(item);
- int rows = names.indexOf(p->qsName);
- int rowidx = rows + item->qlChannels.count();
+ int row = item->insertIndex(p);
- beginInsertRows(index(item), rowidx, rowidx);
+ beginInsertRows(index(c), row, row);
c->addPlayer(p);
- item->qlPlayers.insert(rows, p);
+ item->insertPlayer(p);
endInsertRows();
+
+ if (p->sId == g.sId) {
+ QStack chans;
+ while (c) {
+ chans.push(c);
+ c = c->cParent;
+ }
+ while (! chans.isEmpty()) {
+ c = chans.pop();
+ g.mw->qtvPlayers->setExpanded(index(c), true);
+ }
+ }
}
Player *PlayerModel::addPlayer(short id, QString name) {
Player *p = Player::add(id, this);
p->qsName = name;
+ new ModelItem(p);
+
connect(p, SIGNAL(talkingChanged(bool)), this, SLOT(playerTalkingChanged(bool)));
connect(p, SIGNAL(muteDeafChanged()), this, SLOT(playerMuteDeafChanged()));
@@ -355,10 +514,13 @@ Player *PlayerModel::addPlayer(short id, QString name) {
}
void PlayerModel::removePlayer(Player *p) {
+ ModelItem *item = ModelItem::c_qhPlayers.value(p);
+
hidePlayer(p);
Player::remove(p);
delete p;
+ delete item;
}
void PlayerModel::movePlayer(Player *p, int id) {
@@ -368,71 +530,61 @@ void PlayerModel::movePlayer(Player *p, int id) {
}
void PlayerModel::showChannel(Channel *c, Channel *p) {
- ChannelItem *pitem = p ? qhChannelItems.value(p) : ciRoot;
- ChannelItem *item = qhChannelItems.value(c);
+ ModelItem *item = ModelItem::c_qhChannels.value(p);
- QStringList names;
- foreach(Channel *subc, pitem->qlChannels) {
- names << subc->qsName;
- }
- names << c->qsName;
- qSort(names);
+ Q_ASSERT(p);
+ Q_ASSERT(c);
+ Q_ASSERT(item);
- int rows = names.indexOf(c->qsName);
+ int row = item->insertIndex(c);
- QModelIndex pidx = (pitem==ciRoot) ? QModelIndex() : index(p);
- beginInsertRows(pidx, rows, rows);
- pitem->qlChannels.insert(rows, c);
- item->ciParent = pitem;
+ beginInsertRows(index(p), row, row);
p->addChannel(c);
+ item->insertChannel(c);
endInsertRows();
}
void PlayerModel::hideChannel(Channel *c) {
- ChannelItem *myitem, *pitem;
- Channel *p;
+ Channel *p = c->cParent;
+ ModelItem *item = ModelItem::c_qhChannels.value(p);
- p = Channel::get(c->iParent);
+ int row = item->rowOf(c);
- myitem=qhChannelItems.value(c);
- pitem=qhChannelItems.value(p);
+ unbugHide(index(c));
- int row = pitem->qlChannels.indexOf(c);
- beginRemoveRows(parent(index(myitem)), row, row);
- pitem->qlChannels.removeAt(row);
+ beginRemoveRows(index(p), row, row);
p->removeChannel(c);
- myitem->ciParent = NULL;
+ item->qlChannels.removeAll(c);
endRemoveRows();
}
Channel *PlayerModel::addChannel(int id, Channel *p, QString name) {
Channel *c = Channel::add(id, name, p);
- ChannelItem *ci = new ChannelItem(NULL, c);
- qhChannelItems[c] = ci;
+
+ new ModelItem(c);
showChannel(c, p);
return c;
}
void PlayerModel::removeChannel(Channel *c) {
- ChannelItem *myitem;
+ ModelItem *item;
Player *pl;
Channel *subc;
- myitem=qhChannelItems.value(c);
+ item=ModelItem::c_qhChannels.value(c);
- foreach(subc, myitem->qlChannels)
+ foreach(subc, item->qlChannels)
removeChannel(subc);
- foreach(pl, myitem->qlPlayers)
+ foreach(pl, item->qlPlayers)
removePlayer(pl);
hideChannel(c);
Channel::remove(c);
- delete myitem;
+ delete item;
delete c;
- qhChannelItems.remove(c);
}
void PlayerModel::moveChannel(Channel *c, int id) {
@@ -442,14 +594,13 @@ void PlayerModel::moveChannel(Channel *c, int id) {
}
void PlayerModel::removeAll() {
- ChannelItem *item = ciRoot;
- Player *p;
+ ModelItem *item = miRoot;
while (item->qlChannels.count() > 0)
removeChannel(item->qlChannels[0]);
- foreach(p, item->qlPlayers)
- removePlayer(p);
+ while (item->qlPlayers.count() > 0)
+ removePlayer(item->qlPlayers[0]);
}
Player *PlayerModel::getPlayer(const QModelIndex &idx) const
@@ -457,16 +608,10 @@ Player *PlayerModel::getPlayer(const QModelIndex &idx) const
if (! idx.isValid())
return NULL;
- ChannelItem *item;
- item = static_cast(idx.internalPointer());
+ ModelItem *item;
+ item = static_cast(idx.internalPointer());
- int row = idx.row();
-
- if (row < item->qlChannels.count())
- return NULL;
-
- row-=item->qlChannels.count();
- return item->qlPlayers[row];
+ return item->pPlayer;
}
Channel *PlayerModel::getChannel(const QModelIndex &idx) const
@@ -474,15 +619,13 @@ Channel *PlayerModel::getChannel(const QModelIndex &idx) const
if (! idx.isValid())
return NULL;
- ChannelItem *item;
- item = static_cast(idx.internalPointer());
+ ModelItem *item;
+ item = static_cast(idx.internalPointer());
- int row = idx.row();
-
- if (row < item->qlChannels.count())
- return item->qlChannels[row];
-
- return item->c;
+ if (item->pPlayer)
+ return item->pPlayer->cChannel;
+ else
+ return item->cChan;
}
void PlayerModel::playerTalkingChanged(bool bTalking)
@@ -551,12 +694,7 @@ bool PlayerModel::dropMimeData (const QMimeData *md, Qt::DropAction action, int
if ( ! p.isValid()) {
c = Channel::get(0);
} else {
- ChannelItem *item;
- item = static_cast(p.internalPointer());
- if (p.row() >= 0 && p.row() < item->qlChannels.count())
- c = item->qlChannels[p.row()];
- else
- c = item->c;
+ c = getChannel(p);
}
if (! isChannel) {
diff --git a/PlayerModel.h b/PlayerModel.h
index e9aac7121..f60782d0f 100644
--- a/PlayerModel.h
+++ b/PlayerModel.h
@@ -44,16 +44,37 @@ class PlayerDelegate : public QItemDelegate {
void paint(QPainter * painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
-class ChannelItem {
+struct ModelItem {
friend class PlayerModel;
- protected:
- ChannelItem *ciParent;
- Channel *c;
- QList qlChannels;
- QList qlPlayers;
- ChannelItem(ChannelItem *parent, Channel *c);
- void dump();
+ Channel *cChan;
+ Player *pPlayer;
+ QList qlChannels;
+ QList qlPlayers;
+
+ static QHash c_qhChannels;
+ static QHash c_qhPlayers;
+
+ ModelItem(Channel *c);
+ ModelItem(Player *p);
+ ~ModelItem();
+
+ ModelItem *parent() const;
+ ModelItem *child(int idx) const;
+
+ bool validRow(int idx) const;
+ Player *playerAt(int idx) const;
+ Channel *channelAt(int idx) const;
+ int rowOf(Channel *c) const;
+ int rowOf(Player *p) const;
+ int rowOfSelf() const;
+ int rows() const;
+ int insertIndex(Channel *c) const;
+ int insertIndex(Player *p) const;
+ void insertChannel(Channel *c);
+ void insertPlayer(Player *p);
+
+ bool isValid() const;
};
class PlayerModel : public QAbstractItemModel {
@@ -63,8 +84,8 @@ protected:
QIcon qiTalkingOn, qiTalkingOff;
QIcon qiMutedSelf, qiMutedServer;
QIcon qiDeafenedSelf, qiDeafenedServer;
- QIcon qiAuthenticated;
- ChannelItem *ciRoot;
+ QIcon qiAuthenticated, qiChannel;
+ ModelItem *miRoot;
QHash qhChannelItems;
QModelIndex index(Player *, int column = 0) const;
@@ -78,6 +99,8 @@ protected:
void showChannel(Channel *c, Channel *p);
QString stringIndex(const QModelIndex &index) const;
+
+ void unbugHide(const QModelIndex &index);
public:
PlayerModel(QObject *parent = 0);
~PlayerModel();
@@ -108,7 +131,7 @@ public:
void removeAll();
- QVariant otherRoles(int column, int role) const;
+ QVariant otherRoles(int column, int role, bool isPlayer) const;
public slots:
void playerTalkingChanged(bool talking);
void playerMuteDeafChanged();
diff --git a/ServerDB.cpp b/ServerDB.cpp
index d000b6523..1e60ac9d2 100644
--- a/ServerDB.cpp
+++ b/ServerDB.cpp
@@ -146,7 +146,7 @@ ServerDB::ServerDB() {
if (query.next()) {
int c = query.value(0).toInt();
if (c == 0) {
- query.exec("INSERT INTO acl (channel_id, priority, player_id, group_name, apply_here, apply_sub, grant, revoke) VALUES (0, 1, -1, 'reg', 1, 0, 64, 0)");
+ query.exec("INSERT INTO acl (channel_id, priority, player_id, group_name, apply_here, apply_sub, grant, revoke) VALUES (0, 1, -1, 'auth', 1, 0, 64, 0)");
query.exec("INSERT INTO acl (channel_id, priority, player_id, group_name, apply_here, apply_sub, grant, revoke) VALUES (0, 2, -1, 'admin', 1, 1, 1, 0)");
}
}
diff --git a/mumble.qrc b/mumble.qrc
index 80a5cccd5..c07ae4a42 100644
--- a/mumble.qrc
+++ b/mumble.qrc
@@ -10,6 +10,7 @@
icons/muted_server.png
icons/deafened_server.png
icons/authenticated.png
+ icons/channel.png
icons/config_basic.png
icons/config_dsound.png
icons/config_msgs.png