1 /***************************************************************************
2 * Copyright (C) 2005 by The Quassel Team *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include "channelwidget.h"
29 ChannelWidget::ChannelWidget(QString netname, QString bufname, QString own, QWidget *parent) : QWidget(parent) {
31 _networkName = netname;
32 _bufferName = bufname;
34 ui.ownNick->addItem(own);
35 if(bufname.isEmpty()) {
39 ui.chanSettingsButton->hide();
41 connect(ui.nickTree, SIGNAL(itemExpanded(QTreeWidgetItem *)), this, SLOT(itemExpansionChanged(QTreeWidgetItem*)));
42 connect(ui.nickTree, SIGNAL(itemCollapsed(QTreeWidgetItem *)), this, SLOT(itemExpansionChanged(QTreeWidgetItem*)));
43 connect(ui.inputEdit, SIGNAL(returnPressed()), this, SLOT(enterPressed()));
44 connect(this, SIGNAL(nickListChanged(QStringList)), ui.inputEdit, SLOT(updateNickList(QStringList)));
45 ui.inputEdit->setFocus();
47 opsExpanded = voicedExpanded = usersExpanded = true;
49 // Define standard colors
51 noticeCol = "darkblue";
52 serverCol = "darkblue";
55 quitCol = "firebrick";
56 partCol = "firebrick";
57 kickCol = "firebrick";
63 void ChannelWidget::enterPressed() {
64 QStringList lines = ui.inputEdit->text().split('\n', QString::SkipEmptyParts);
65 foreach(QString msg, lines) {
66 if(msg.isEmpty()) continue;
67 emit sendInput(networkName(), bufferName(), msg);
69 ui.inputEdit->clear();
72 void ChannelWidget::recvMessage(Message msg) {
75 QString user = userFromMask(msg.sender);
76 QString host = hostFromMask(msg.sender);
77 QString nick = nickFromMask(msg.sender);
80 c = stdCol; n = QString("<%1>").arg(nick); s = msg.msg;
83 c = serverCol; s = msg.msg;
86 c = errorCol; s = msg.msg;
90 s = QString(tr("--> %1 (%2@%3) has joined %4")).arg(nick).arg(user).arg(host).arg(bufferName());
94 s = QString(tr("<-- %1 (%2@%3) has left %4")).arg(nick).arg(user).arg(host).arg(bufferName());
95 if(!msg.msg.isEmpty()) s = QString("%1 (%2)").arg(s).arg(msg.msg);
99 QString victim = msg.msg.section(" ", 0, 0);
100 if(victim == ui.ownNick->currentText()) victim = tr("you");
101 QString kickmsg = msg.msg.section(" ", 1);
102 s = QString(tr("--> %1 has kicked %2 from %3")).arg(nick).arg(victim).arg(bufferName());
103 if(!kickmsg.isEmpty()) s = QString("%1 (%2)").arg(s).arg(kickmsg);
108 s = QString(tr("<-- %1 (%2@%3) has quit")).arg(nick).arg(user).arg(host);
109 if(!msg.msg.isEmpty()) s = QString("%1 (%2)").arg(s).arg(msg.msg);
113 if(nick == msg.msg) s = QString(tr("<-> You are now known as %1")).arg(msg.msg);
114 else s = QString(tr("<-> %1 is now known as %2")).arg(nick).arg(msg.msg);
118 if(nick.isEmpty()) s = tr("*** User mode: %1").arg(msg.msg);
119 else s = tr("*** Mode %1 by %2").arg(msg.msg).arg(nick);
122 c = stdCol; n = QString("[%1]").arg(msg.sender); s = msg.msg;
125 s.replace('&', "&"); s.replace('<', "<"); s.replace('>', ">");
126 QString html = QString("<table cellspacing=0 cellpadding=0><tr>"
127 "<td width=50><div style=\"color:%2;\">[%1]</div></td>")
128 .arg(msg.timeStamp.toLocalTime().toString("hh:mm:ss")).arg("darkblue");
130 html += QString("<td width=150><div align=right style=\"white-space:pre;margin-left:6px;color:%2;\">%1</div></td>")
131 .arg(n).arg("royalblue");
132 html += QString("<td><div style=\"white-space:pre-wrap;margin-left:6px;color:%2;\">%1</div></td>""</tr></table>").arg(s).arg(c);
133 ui.chatWidget->append(html); // qDebug() << html;
134 //ui.chatWidget->append(QString("<table border=1 cellspacing=0 cellpadding=0><tr><td>%1</td><td width=100 style=border-right-width:1px;><div style=margin-left:8px; margin-right:8px;>%2</div></td><td style=color:firebrick> %3</td></tr></table>")
135 //.arg(msg.timeStamp.toLocalTime().toString("hh:mm:ss")).arg(nick).arg(s));
136 ui.chatWidget->ensureCursorVisible();
139 void ChannelWidget::recvStatusMsg(QString msg) {
140 ui.chatWidget->insertPlainText(QString("[STATUS] %1").arg(msg));
141 ui.chatWidget->ensureCursorVisible();
144 void ChannelWidget::setTopic(QString topic) {
145 ui.topicEdit->setText(topic);
148 void ChannelWidget::setNicks(QStringList nicks) {
153 void ChannelWidget::addNick(QString nick, VarMap props) {
156 if(completer) delete completer;
157 completer = new QCompleter(nicks.keys());
158 ui.inputEdit->setCompleter(completer);
161 void ChannelWidget::updateNick(QString nick, VarMap props) {
166 void ChannelWidget::renameNick(QString oldnick, QString newnick) {
167 QVariant v = nicks.take(oldnick);
172 void ChannelWidget::removeNick(QString nick) {
177 void ChannelWidget::setOwnNick(QString nick) {
179 ui.ownNick->addItem(nick);
182 void ChannelWidget::updateNickList() {
183 ui.nickTree->clear();
184 if(nicks.count() != 1) ui.nickTree->setHeaderLabel(tr("%1 Users").arg(nicks.count()));
185 else ui.nickTree->setHeaderLabel(tr("1 User"));
186 QTreeWidgetItem *ops = new QTreeWidgetItem();
187 QTreeWidgetItem *voiced = new QTreeWidgetItem();
188 QTreeWidgetItem *users = new QTreeWidgetItem();
189 // To sort case-insensitive, we have to put all nicks in a map which is sorted by (lowercase) key...
190 QMap<QString, QString> sorted;
191 foreach(QString n, nicks.keys()) { sorted[n.toLower()] = n; }
192 foreach(QString n, sorted.keys()) {
193 QString nick = sorted[n];
194 QString mode = nicks[nick].toMap()["Channels"].toMap()[bufferName()].toMap()["Mode"].toString();
195 if(mode.contains('o')) { new QTreeWidgetItem(ops, QStringList(QString("@%1").arg(nick))); }
196 else if(mode.contains('v')) { new QTreeWidgetItem(voiced, QStringList(QString("+%1").arg(nick))); }
197 else new QTreeWidgetItem(users, QStringList(nick));
199 if(ops->childCount()) {
200 ops->setText(0, tr("%1 Operators").arg(ops->childCount()));
201 ui.nickTree->addTopLevelItem(ops);
202 ops->setExpanded(opsExpanded);
204 if(voiced->childCount()) {
205 voiced->setText(0, tr("%1 Voiced").arg(voiced->childCount()));
206 ui.nickTree->addTopLevelItem(voiced);
207 voiced->setExpanded(voicedExpanded);
208 } else delete voiced;
209 if(users->childCount()) {
210 users->setText(0, tr("%1 Users").arg(users->childCount()));
211 ui.nickTree->addTopLevelItem(users);
212 users->setExpanded(usersExpanded);
216 void ChannelWidget::itemExpansionChanged(QTreeWidgetItem *item) {
217 if(item->child(0)->text(0).startsWith('@')) opsExpanded = item->isExpanded();
218 else if(item->child(0)->text(0).startsWith('+')) voicedExpanded = item->isExpanded();
219 else usersExpanded = item->isExpanded();
222 /**********************************************************************************************/
225 IrcWidget::IrcWidget(QWidget *parent) : QWidget(parent) {
227 ui.tabWidget->removeTab(0);
229 connect(guiProxy, SIGNAL(csDisplayMsg(QString, QString, Message)), this, SLOT(recvMessage(QString, QString, Message)));
230 connect(guiProxy, SIGNAL(csDisplayStatusMsg(QString, QString)), this, SLOT(recvStatusMsg(QString, QString)));
231 connect(guiProxy, SIGNAL(csTopicSet(QString, QString, QString)), this, SLOT(setTopic(QString, QString, QString)));
232 connect(guiProxy, SIGNAL(csSetNicks(QString, QString, QStringList)), this, SLOT(setNicks(QString, QString, QStringList)));
233 connect(guiProxy, SIGNAL(csNickAdded(QString, QString, VarMap)), this, SLOT(addNick(QString, QString, VarMap)));
234 connect(guiProxy, SIGNAL(csNickRemoved(QString, QString)), this, SLOT(removeNick(QString, QString)));
235 connect(guiProxy, SIGNAL(csNickRenamed(QString, QString, QString)), this, SLOT(renameNick(QString, QString, QString)));
236 connect(guiProxy, SIGNAL(csNickUpdated(QString, QString, VarMap)), this, SLOT(updateNick(QString, QString, VarMap)));
237 connect(guiProxy, SIGNAL(csOwnNickSet(QString, QString)), this, SLOT(setOwnNick(QString, QString)));
238 connect(this, SIGNAL(sendInput( QString, QString, QString )), guiProxy, SLOT(gsUserInput(QString, QString, QString)));
241 ChannelWidget * IrcWidget::getBuffer(QString net, QString buf) {
242 QString key = net + buf;
243 if(!buffers.contains(key)) {
244 ChannelWidget *cw = new ChannelWidget(net, buf, ownNick[net]);
245 connect(cw, SIGNAL(sendInput(QString, QString, QString)), this, SLOT(userInput(QString, QString, QString)));
246 ui.tabWidget->addTab(cw, net+buf);
247 ui.tabWidget->setCurrentWidget(cw);
255 void IrcWidget::recvMessage(QString net, QString buf, Message msg) {
256 ChannelWidget *cw = getBuffer(net, buf);
257 cw->recvMessage(msg);
260 void IrcWidget::recvStatusMsg(QString net, QString msg) {
261 recvMessage(net, "", Message(Message::Server, QString("[STATUS] %1").arg(msg)));
265 void IrcWidget::userInput(QString net, QString buf, QString msg) {
266 emit sendInput(net, buf, msg);
269 void IrcWidget::setTopic(QString net, QString buf, QString topic) {
270 ChannelWidget *cw = getBuffer(net, buf);
274 void IrcWidget::setNicks(QString net, QString buf, QStringList nicks) {
275 ChannelWidget *cw = getBuffer(net, buf);
279 void IrcWidget::addNick(QString net, QString nick, VarMap props) {
280 VarMap netnicks = nicks[net].toMap();
281 netnicks[nick] = props;
282 nicks[net] = netnicks;
283 VarMap chans = props["Channels"].toMap();
284 QStringList c = chans.keys();
285 foreach(QString bufname, c) {
286 getBuffer(net, bufname)->addNick(nick, props);
290 void IrcWidget::renameNick(QString net, QString oldnick, QString newnick) {
291 VarMap netnicks = nicks[net].toMap();
292 qDebug() << "renNICK:"<<oldnick<<newnick;
293 Q_ASSERT(netnicks.contains(oldnick));
294 QStringList chans = netnicks[oldnick].toMap()["Channels"].toMap().keys(); qDebug() << "l:" << chans;
295 foreach(QString c, chans) {
296 qDebug() << net << c;
297 getBuffer(net, c)->renameNick(oldnick, newnick);
299 QVariant v = netnicks.take(oldnick);
300 netnicks[newnick] = v;
301 nicks[net] = netnicks;
304 void IrcWidget::updateNick(QString net, QString nick, VarMap props) {
305 QStringList oldchans = nicks[net].toMap()[nick].toMap()["Channels"].toMap().keys();
306 QStringList newchans = props["Channels"].toMap().keys();
307 foreach(QString c, newchans) {
308 if(oldchans.contains(c)) getBuffer(net, c)->updateNick(nick, props);
309 else getBuffer(net, c)->addNick(nick, props);
311 foreach(QString c, oldchans) {
312 if(!newchans.contains(c)) getBuffer(net, c)->removeNick(nick);
314 VarMap netnicks = nicks[net].toMap();
315 netnicks[nick] = props;
316 nicks[net] = netnicks;
319 void IrcWidget::removeNick(QString net, QString nick) {
320 VarMap chans = nicks[net].toMap()[nick].toMap()["Channels"].toMap();
321 foreach(QString bufname, chans.keys()) {
322 getBuffer(net, bufname)->removeNick(nick);
324 VarMap netnicks = nicks[net].toMap();
325 netnicks.remove(nick);
326 nicks[net] = netnicks;
329 void IrcWidget::setOwnNick(QString net, QString nick) {
331 foreach(ChannelWidget *cw, buffers.values()) {
332 if(cw->networkName() == net) cw->setOwnNick(nick);