From cb84309bf4e0cbcb97d227a9ff6284d5c79f23ea Mon Sep 17 00:00:00 2001 From: Thorvald Natvig Date: Tue, 27 Sep 2005 18:28:04 +0000 Subject: [PATCH] Rewrote the player model, should be more "in tune" with Qts method of referencing items now. Made the iconlist in the config dialog resizable. Updated "reg" to "auth" in a few places. git-svn-id: https://mumble.svn.sourceforge.net/svnroot/mumble/trunk@220 05730e5d-ab1b-0410-a4ac-84af385074fa --- ACLEditor.cpp | 4 +- ConfigDialog.cpp | 16 +- DXAudioOutput.cpp | 2 +- Group.cpp | 2 +- INSTALL | 8 +- MainWindow.cpp | 51 +++-- MainWindow.h | 8 +- PlayerModel.cpp | 518 +++++++++++++++++++++++++++++----------------- PlayerModel.h | 45 +++- ServerDB.cpp | 2 +- mumble.qrc | 1 + 11 files changed, 420 insertions(+), 237 deletions(-) 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