Drop some more data from Channel on disconnect.
[konversation:konversation.git] / src / irc / channel.cpp
1 /*
2   This program is free software; you can redistribute it and/or modify
3   it under the terms of the GNU General Public License as published by
4   the Free Software Foundation; either version 2 of the License, or
5   (at your option) any later version.
6 */
7
8 /*
9   Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
10   Copyright (C) 2004-2006, 2009 Peter Simonsson <peter.simonsson@gmail.com>
11   Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
12 */
13
14 #define QT3_SUPPORT //TODO remove when porting away from Q3*
15
16 #include "channel.h"
17 #include "channeloptionsdialog.h"
18 #include "application.h"
19 #include "server.h"
20 #include "nick.h"
21 #include "nicklistview.h"
22 #include "quickbutton.h"
23 #include "modebutton.h"
24 #include "ircinput.h"
25 #include "ircviewbox.h"
26 #include "ircview.h"
27 #include <kabc/addressbook.h>
28 #include <kabc/stdaddressbook.h>
29 #include "common.h"
30 #include "topiclabel.h"
31 #include "notificationhandler.h"
32 #include "viewcontainer.h"
33 #include "linkaddressbook/linkaddressbookui.h"
34 #include "linkaddressbook/addressbook.h"
35
36 #include <QEvent>
37 #include <QSizePolicy>
38 #include <QRegExp>
39 #include <QSplitter>
40 #include <QCheckBox>
41 #include <QTimer>
42 #include <QToolButton>
43 #include <QHeaderView>
44
45 #include <KLineEdit>
46 #include <KInputDialog>
47 #include <KPasswordDialog>
48 #include <KGlobalSettings>
49 #include <KMessageBox>
50 #include <KIconLoader>
51 #include <KColorScheme>
52 #include <KVBox>
53 #include <KHBox>
54 #include <KComboBox>
55
56
57 #define DELAYED_SORT_TRIGGER    10
58
59 using namespace Konversation;
60
61 bool nickTimestampLessThan(const Nick* nick1, const Nick* nick2)
62 {
63     int returnValue = nick2->getChannelNick()->timeStamp() - nick1->getChannelNick()->timeStamp();
64     if( returnValue == 0) {
65         returnValue = QString::compare(nick1->getChannelNick()->loweredNickname(),
66                                        nick2->getChannelNick()->loweredNickname());
67     }
68
69     return (returnValue < 0);
70 }
71
72 bool nickLessThan(const Nick* nick1, const Nick* nick2)
73 {
74     return nick1->getChannelNick()->loweredNickname() < nick2->getChannelNick()->loweredNickname();
75 }
76
77
78 using Konversation::ChannelOptionsDialog;
79
80 Channel::Channel(QWidget* parent, QString _name) : ChatWindow(parent)
81 {
82     // init variables
83
84     //HACK I needed the channel name at time of setServer, but setName needs m_server..
85     //     This effectively assigns the name twice, but none of the other logic has been moved or updated.
86     name=_name;
87     m_ownChannelNick = 0;
88     m_processingTimer = 0;
89     m_optionsDialog = NULL;
90     m_delayedSortTimer = 0;
91     m_delayedSortTrigger = 0;
92     m_pendingChannelNickLists.clear();
93     m_currentIndex = 0;
94     m_opsToAdd = 0;
95     nicks = 0;
96     ops = 0;
97     completionPosition = 0;
98     nickChangeDialog = 0;
99     channelCommand = false;
100     m_nicknameListViewTextChanged = 0;
101
102     m_joined = false;
103
104     quickButtonsChanged = false;
105     quickButtonsState = false;
106
107     modeButtonsChanged = false;
108     modeButtonsState = false;
109
110     awayChanged = false;
111     awayState = false;
112
113     splittersInitialized = false;
114     topicSplitterHidden = false;
115     channelSplitterHidden = false;
116
117     // flag for first seen topic
118     topicAuthorUnknown = true;
119
120     setType(ChatWindow::Channel);
121
122     setChannelEncodingSupported(true);
123
124     // Build some size policies for the widgets
125     QSizePolicy hfixed = QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
126     QSizePolicy hmodest = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
127     QSizePolicy vmodest = QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
128     QSizePolicy vfixed = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
129     QSizePolicy modest = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
130     QSizePolicy greedy = QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
131
132     m_vertSplitter = new QSplitter(Qt::Vertical, this);
133     m_vertSplitter->setOpaqueResize(KGlobalSettings::opaqueResize());
134
135
136     QWidget* topicWidget = new QWidget(m_vertSplitter);
137     m_vertSplitter->setStretchFactor(m_vertSplitter->indexOf(topicWidget), 0);
138
139     QGridLayout* topicLayout = new QGridLayout(topicWidget);
140     topicLayout->setMargin(0);
141     topicLayout->setSpacing(0);
142
143     m_topicButton = new QToolButton(topicWidget);
144     m_topicButton->setIcon(KIcon("document-edit"));
145     m_topicButton->setToolTip(i18n("Edit Channel Settings"));
146     m_topicButton->setAutoRaise(true);
147     connect(m_topicButton, SIGNAL(clicked()), this, SLOT(showOptionsDialog()));
148
149     topicLine = new Konversation::TopicLabel(topicWidget);
150     topicLine->setChannelName(getName());
151     topicLine->setWordWrap(true);
152     topicLine->setWhatsThis(i18n("<qt><p>Every channel on IRC has a topic associated with it.  This is simply a message that everybody can see.</p><p>If you are an operator, or the channel mode <em>'T'</em> has not been set, then you can change the topic by clicking the Edit Channel Properties button to the left of the topic.  You can also view the history of topics there.</p></qt>"));
153     connect(topicLine, SIGNAL(setStatusBarTempText(const QString&)), this, SIGNAL(setStatusBarTempText(const QString&)));
154     connect(topicLine, SIGNAL(clearStatusBarTempText()), this, SIGNAL(clearStatusBarTempText()));
155
156     topicLayout->addWidget(m_topicButton, 0, 0);
157     topicLayout->addWidget(topicLine, 0, 1, -1, 1);
158
159     // The box holding the channel modes
160     modeBox = new KHBox(topicWidget);
161     modeBox->hide();
162     modeBox->setSizePolicy(hfixed);
163     modeT = new ModeButton("T",modeBox,0);
164     modeN = new ModeButton("N",modeBox,1);
165     modeS = new ModeButton("S",modeBox,2);
166     modeI = new ModeButton("I",modeBox,3);
167     modeP = new ModeButton("P",modeBox,4);
168     modeM = new ModeButton("M",modeBox,5);
169     modeK = new ModeButton("K",modeBox,6);
170     modeL = new ModeButton("L",modeBox,7);
171
172     modeT->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('T'));
173     modeN->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('N'));
174     modeS->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('S'));
175     modeI->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('I'));
176     modeP->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('P'));
177     modeM->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('M'));
178     modeK->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('K'));
179     modeL->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('L'));
180
181     connect(modeT,SIGNAL(clicked(int,bool)),this,SLOT(modeButtonClicked(int,bool)));
182     connect(modeN,SIGNAL(clicked(int,bool)),this,SLOT(modeButtonClicked(int,bool)));
183     connect(modeS,SIGNAL(clicked(int,bool)),this,SLOT(modeButtonClicked(int,bool)));
184     connect(modeI,SIGNAL(clicked(int,bool)),this,SLOT(modeButtonClicked(int,bool)));
185     connect(modeP,SIGNAL(clicked(int,bool)),this,SLOT(modeButtonClicked(int,bool)));
186     connect(modeM,SIGNAL(clicked(int,bool)),this,SLOT(modeButtonClicked(int,bool)));
187     connect(modeK,SIGNAL(clicked(int,bool)),this,SLOT(modeButtonClicked(int,bool)));
188     connect(modeL,SIGNAL(clicked(int,bool)),this,SLOT(modeButtonClicked(int,bool)));
189
190     limit=new KLineEdit(modeBox);
191     limit->setToolTip(i18n("Maximum users allowed in channel"));
192     limit->setWhatsThis(i18n("<qt><p>This is the channel user limit - the maximum number of users that can be in the channel at a time.  If you are an operator, you can set this.  The channel mode <b>T</b>opic (button to left) will automatically be set if set this.</p></qt>"));
193     connect(limit,SIGNAL (returnPressed()),this,SLOT (channelLimitChanged()) );
194     connect(limit,SIGNAL (editingFinished()), this, SLOT(channelLimitChanged()) );
195
196     topicLayout->addWidget(modeBox, 0, 2);
197     topicLayout->setRowStretch(1, 10);
198     topicLayout->setColumnStretch(1, 10);
199
200     showTopic(Preferences::self()->showTopic());
201     showModeButtons(Preferences::self()->showModeButtons());
202
203     // (this) The main Box, holding the channel view/topic and the input line
204     m_horizSplitter = new QSplitter(m_vertSplitter);
205     m_vertSplitter->setStretchFactor(m_vertSplitter->indexOf(m_horizSplitter), 1);
206     m_horizSplitter->setOpaqueResize( KGlobalSettings::opaqueResize() );
207
208     // Server will be set later in setServer()
209     IRCViewBox* ircViewBox = new IRCViewBox(m_horizSplitter, NULL);
210     m_horizSplitter->setStretchFactor(m_horizSplitter->indexOf(ircViewBox), 1);
211     setTextView(ircViewBox->ircView());
212     connect(textView,SIGNAL(popupCommand(int)),this,SLOT(popupChannelCommand(int)));
213
214     // The box that holds the Nick List and the quick action buttons
215     nickListButtons = new KVBox(m_horizSplitter);
216     m_horizSplitter->setStretchFactor(m_horizSplitter->indexOf(nickListButtons), 0);
217     nickListButtons->setSpacing(spacing());
218
219     nicknameListView=new NickListView(nickListButtons, this);
220     nicknameListView->setSelectionMode(QAbstractItemView::ExtendedSelection);
221     nicknameListView->setAllColumnsShowFocus(true);
222
223     nicknameListView->header()->hide();
224
225     nicknameListView->header()->setStretchLastSection(false);
226
227     nicknameListView->installEventFilter(this);
228
229     // initialize buttons grid, will be set up in updateQuickButtons
230     m_buttonsGrid = 0;
231
232     // The box holding the Nickname button and Channel input
233     commandLineBox = new KHBox(this);
234     commandLineBox->setSpacing(spacing());
235
236     nicknameCombobox = new KComboBox(commandLineBox);
237     nicknameCombobox->setEditable(true);
238     nicknameCombobox->setSizeAdjustPolicy(KComboBox::AdjustToContents);
239     KLineEdit* nicknameComboboxLineEdit = qobject_cast<KLineEdit*>(nicknameCombobox->lineEdit());
240     if (nicknameComboboxLineEdit) nicknameComboboxLineEdit->setClearButtonShown(false);
241     nicknameCombobox->setWhatsThis(i18n("<qt><p>This shows your current nick, and any alternatives you have set up.  If you select or type in a different nickname, then a request will be sent to the IRC server to change your nick.  If the server allows it, the new nickname will be selected.  If you type in a new nickname, you need to press 'Enter' at the end.</p><p>You can add change the alternative nicknames from the <em>Identities</em> option in the <em>File</em> menu.</p></qt>"));
242
243     awayLabel = new QLabel(i18n("(away)"), commandLineBox);
244     awayLabel->hide();
245     cipherLabel = new QLabel(commandLineBox);
246     cipherLabel->hide();
247     cipherLabel->setPixmap(KIconLoader::global()->loadIcon("document-encrypt", KIconLoader::Toolbar));
248     channelInput = new IRCInput(commandLineBox);
249
250     getTextView()->installEventFilter(channelInput);
251     topicLine->installEventFilter(channelInput);
252     channelInput->installEventFilter(this);
253
254     // Set the widgets size policies
255     m_topicButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
256     topicLine->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum));
257
258     commandLineBox->setSizePolicy(vfixed);
259
260     limit->setMaximumSize(40,100);
261     limit->setSizePolicy(hfixed);
262
263     modeT->setMaximumSize(20,100);
264     modeT->setSizePolicy(hfixed);
265     modeN->setMaximumSize(20,100);
266     modeN->setSizePolicy(hfixed);
267     modeS->setMaximumSize(20,100);
268     modeS->setSizePolicy(hfixed);
269     modeI->setMaximumSize(20,100);
270     modeI->setSizePolicy(hfixed);
271     modeP->setMaximumSize(20,100);
272     modeP->setSizePolicy(hfixed);
273     modeM->setMaximumSize(20,100);
274     modeM->setSizePolicy(hfixed);
275     modeK->setMaximumSize(20,100);
276     modeK->setSizePolicy(hfixed);
277     modeL->setMaximumSize(20,100);
278     modeL->setSizePolicy(hfixed);
279
280     getTextView()->setSizePolicy(greedy);
281     nicknameListView->setSizePolicy(hmodest);
282
283     connect(channelInput,SIGNAL (submit()),this,SLOT (channelTextEntered()) );
284     connect(channelInput,SIGNAL (envelopeCommand()),this,SLOT (channelPassthroughCommand()) );
285     connect(channelInput,SIGNAL (nickCompletion()),this,SLOT (completeNick()) );
286     connect(channelInput,SIGNAL (endCompletion()),this,SLOT (endCompleteNick()) );
287     connect(channelInput,SIGNAL (textPasted(const QString&)),this,SLOT (textPasted(const QString&)) );
288
289     connect(getTextView(), SIGNAL(textPasted(bool)), channelInput, SLOT(paste(bool)));
290     connect(getTextView(),SIGNAL (gotFocus()),channelInput,SLOT (setFocus()) );
291     connect(getTextView(),SIGNAL (sendFile()),this,SLOT (sendFileMenu()) );
292     connect(getTextView(),SIGNAL (autoText(const QString&)),this,SLOT (sendChannelText(const QString&)) );
293
294     connect(nicknameListView,SIGNAL (popupCommand(int)),this,SLOT (popupCommand(int)) );
295     connect(nicknameListView,SIGNAL (itemDoubleClicked(QTreeWidgetItem*,int)),this,SLOT (doubleClickCommand(QTreeWidgetItem*,int)) );
296     connect(nicknameCombobox,SIGNAL (activated(int)),this,SLOT(nicknameComboboxChanged()));
297
298     if(nicknameCombobox->lineEdit())
299         connect(nicknameCombobox->lineEdit(), SIGNAL (editingFinished()),this,SLOT(nicknameComboboxChanged()));
300
301
302     connect(&userhostTimer,SIGNAL (timeout()),this,SLOT (autoUserhost()));
303
304     // every few seconds try to get more userhosts
305     userhostTimer.start(10000);
306
307     m_whoTimer.setSingleShot(true);
308     connect(&m_whoTimer,SIGNAL (timeout()),this,SLOT (autoWho()));
309
310     // every 5 minutes decrease everyone's activity by 1 unit
311     m_fadeActivityTimer.start(5*60*1000);
312
313     connect(&m_fadeActivityTimer, SIGNAL(timeout()), this, SLOT(fadeActivity()));
314
315     // re-schedule when the settings were changed
316     connect(Preferences::self(), SIGNAL (autoContinuousWhoChanged()),this,SLOT (scheduleAutoWho()));
317
318     updateAppearance();
319
320     #ifdef HAVE_QCA2
321     m_cipher = 0;
322     #endif
323     //FIXME JOHNFLUX
324     // connect( Konversation::Addressbook::self()->getAddressBook(), SIGNAL( addressBookChanged( AddressBook * ) ), this, SLOT( slotLoadAddressees() ) );
325     // connect( Konversation::Addressbook::self(), SIGNAL(addresseesChanged()), this, SLOT(slotLoadAddressees()));
326
327     // Setup delayed sort timer
328     m_delayedSortTimer = new QTimer(this);
329     m_delayedSortTimer->setSingleShot(true);
330     connect(m_delayedSortTimer, SIGNAL(timeout()), this, SLOT(delayedSortNickList()));
331 }
332
333 //FIXME there is some logic in setLogfileName that needs to be split out and called here if the server display name gets changed
334 void Channel::setServer(Server* server)
335 {
336     if (m_server != server)
337     {
338         connect(server, SIGNAL(connectionStateChanged(Server*, Konversation::ConnectionState)),
339                 SLOT(connectionStateChanged(Server*, Konversation::ConnectionState)));
340         connect(server, SIGNAL(nickInfoChanged()),
341                 this, SLOT(updateNickInfos()));
342         connect(server, SIGNAL(channelNickChanged(const QString&)),
343                 this, SLOT(updateChannelNicks(const QString&)));
344     }
345
346     ChatWindow::setServer(server);
347     if (!server->getKeyForRecipient(getName()).isEmpty())
348         cipherLabel->show();
349     topicLine->setServer(server);
350     refreshModeButtons();
351     nicknameCombobox->setModel(m_server->nickListModel());
352 }
353
354 void Channel::connectionStateChanged(Server* server, Konversation::ConnectionState state)
355 {
356     if (server == m_server)
357     {
358         if (state !=  Konversation::SSConnected)
359         {
360             m_joined = false;
361
362             //HACK the way the notification priorities work sucks, this forces the tab text color to gray right now.
363             if (m_currentTabNotify == Konversation::tnfNone || (!Preferences::self()->tabNotificationsEvents() && m_currentTabNotify == Konversation::tnfControl))
364                 Application::instance()->getMainWindow()->getViewContainer()->unsetViewNotification(this);
365         }
366     }
367 }
368
369 void Channel::setEncryptedOutput(bool e)
370 {
371
372     if (e) {
373     #ifdef HAVE_QCA2
374         cipherLabel->show();
375         //scan the channel topic and decrypt it if necessary
376         if (m_topicHistory.isEmpty())
377             return;
378         QString topic(m_topicHistory.at(0).section(' ',2));
379
380         //prepend two colons to make it appear to be an irc message for decryption,
381         // \r because it won't decrypt without it, even though the message did not have a \r
382         // when encrypted. Bring on the QCA!
383         //QByteArray cipher = "::" + topic.toUtf8() + '\x0d';
384         QByteArray cipherText = topic.toUtf8();
385         QByteArray key = m_server->getKeyForRecipient(getName());
386
387         if(getCipher()->setKey(key))
388             cipherText = getCipher()->decryptTopic(cipherText);
389
390         topic=QString::fromUtf8(cipherText.data()+2, cipherText.length()-2);
391         m_topicHistory[0] = m_topicHistory[0].section(' ', 0, 1) + ' ' + topic;
392         topicLine->setText(topic);
393         emit topicHistoryChanged();
394     #endif
395     }
396     else
397         cipherLabel->hide();
398 }
399
400 Channel::~Channel()
401 {
402     kDebug() << "(" << getName() << ")";
403
404     // Purge nickname list
405     purgeNicks();
406     kDebug() << "nicks purged";
407
408     // Unlink this channel from channel list
409     m_server->removeChannel(this);
410     kDebug() << "Channel removed.";
411
412 }
413
414 bool Channel::rejoinable()
415 {
416     if (getServer() && getServer()->isConnected())
417         return !m_joined;
418
419     return false;
420 }
421
422 void Channel::rejoin()
423 {
424     if (rejoinable())
425         m_server->sendJoinCommand(getName(), getPassword());
426 }
427
428 ChannelNickPtr Channel::getOwnChannelNick()
429 {
430     return m_ownChannelNick;
431 }
432
433 ChannelNickPtr Channel::getChannelNick(const QString &ircnick)
434 {
435     return m_server->getChannelNick(getName(), ircnick);
436 }
437
438 void Channel::purgeNicks()
439 {
440     m_ownChannelNick = 0;
441
442     // Purge nickname list
443     qDeleteAll(nicknameList);
444     nicknameList.clear();
445
446     // Execute this otherwise it may crash trying to access
447     // deleted nicks
448     nicknameListView->executeDelayedItemsLayout();
449
450     // clear stats counter
451     nicks=0;
452     ops=0;
453 }
454
455 void Channel::showOptionsDialog()
456 {
457     if(!m_optionsDialog)
458         m_optionsDialog = new Konversation::ChannelOptionsDialog(this);
459
460     m_optionsDialog->refreshAllowedChannelModes();
461     m_optionsDialog->refreshModes();
462     m_optionsDialog->refreshTopicHistory();
463     m_optionsDialog->show();
464 }
465
466 void Channel::textPasted(const QString& text)
467 {
468     if(m_server)
469     {
470         QStringList multiline = text.split('\n', QString::SkipEmptyParts);
471         for(int index=0;index<multiline.count();index++)
472         {
473             QString line=multiline[index];
474             QString cChar(Preferences::self()->commandChar());
475             // make sure that lines starting with command char get escaped
476             if(line.startsWith(cChar)) line=cChar+line;
477             sendChannelText(line);
478         }
479     }
480 }
481
482 // Will be connected to IRCView::popupCommand(int)
483 void Channel::popupChannelCommand(int id)
484 {
485     channelCommand = true; // Context menu executed from ircview
486     popupCommand(id);
487     textView->clearContextNick();
488     channelCommand = false;
489 }
490
491 // Will be connected to NickListView::popupCommand(int)
492 void Channel::popupCommand(int id)
493 {
494     QString pattern;
495     QString cc = Preferences::self()->commandChar();
496     QString args;
497     QString question;
498     bool raw=false;
499     QString mode;
500     QStringList nickList = getSelectedNickList();
501
502     switch(id)
503     {
504         case Konversation::AddressbookEdit:
505         {
506             ChannelNickList nickList=getSelectedChannelNicks();
507             for(ChannelNickList::ConstIterator it=nickList.constBegin();it!=nickList.constEnd();++it)
508             {
509                 KABC::Addressee addressee = (*it)->getNickInfo()->getAddressee();
510                 if(addressee.isEmpty()) break;
511
512                 Konversation::Addressbook::self()->editAddressee(addressee.uid());
513             }
514             break;
515         }
516         case Konversation::AddressbookNew:
517         case Konversation::AddressbookDelete:
518         {
519             Konversation::Addressbook *addressbook = Konversation::Addressbook::self();
520             ChannelNickList nickList=getSelectedChannelNicks();
521             //Handle all the selected nicks in one go.  Either they all save, or none do.
522             if(addressbook->getAndCheckTicket())
523             {
524                 for(ChannelNickList::ConstIterator it=nickList.constBegin();it!=nickList.constEnd();++it)
525                 {
526                     if(id == Konversation::AddressbookDelete)
527                     {
528                         KABC::Addressee addr = (*it)->getNickInfo()->getAddressee();
529                         addressbook->unassociateNick(addr, (*it)->getNickname(), m_server->getServerName(), m_server->getDisplayName());
530                     }
531                     else
532                     {
533                         //make new addressbook contact
534                         KABC::Addressee addr;
535                         NickInfoPtr nickInfo = (*it)->getNickInfo();
536                         if(nickInfo->getRealName().isEmpty())
537                             addr.setGivenName(nickInfo->getNickname());
538                         else
539                             addr.setGivenName(nickInfo->getRealName());
540                         addr.setNickName(nickInfo->getNickname());
541                         addressbook->associateNickAndUnassociateFromEveryoneElse(addr, (*it)->getNickname(), m_server->getServerName(), m_server->getDisplayName());
542                     }
543                 }
544                 addressbook->saveTicket(); // This will refresh the nicks automatically for us. At least, if it doesn't, it's a bug :)
545             }
546             break;
547         }
548         case Konversation::AddressbookChange:
549         {
550             ChannelNickList nickList=getSelectedChannelNicks();
551             for(ChannelNickList::ConstIterator it=nickList.constBegin();it!=nickList.constEnd();++it)
552             {
553                 LinkAddressbookUI *linkaddressbookui = new LinkAddressbookUI(this, (*it)->getNickInfo()->getNickname(), m_server->getServerName(),
554                                                                              m_server->getDisplayName(), (*it)->getNickInfo()->getRealName());
555                 linkaddressbookui->show();
556             }
557             break;
558         }
559         case Konversation::SendEmail:
560         {
561             Konversation::Addressbook::self()->sendEmail(getSelectedChannelNicks());
562             break;
563         }
564         case Konversation::GiveOp:
565             pattern="MODE %c +%m %l";
566             mode='o';
567             raw=true;
568             break;
569         case Konversation::TakeOp:
570             pattern="MODE %c -%m %l";
571             mode='o';
572             raw=true;
573             break;
574         case Konversation::GiveHalfOp:
575             pattern="MODE %c +%m %l";
576             mode='h';
577             raw=true;
578             break;
579         case Konversation::TakeHalfOp:
580             pattern="MODE %c -%m %l";
581             mode='h';
582             raw=true;
583             break;
584         case Konversation::GiveVoice:
585             pattern="MODE %c +%m %l";
586             mode='v';
587             raw=true;
588             break;
589         case Konversation::TakeVoice:
590             pattern="MODE %c -%m %l";
591             mode='v';
592             raw=true;
593             break;
594         case Konversation::Version:
595             pattern=cc+"CTCP %u VERSION";
596             break;
597         case Konversation::Whois:
598             pattern="WHOIS %u %u";
599             raw=true;
600             break;
601         case Konversation::Topic:
602             m_server->requestTopic(getTextView()->currentChannel());
603             break;
604         case Konversation::Names:
605             m_server->queue("NAMES " + getTextView()->currentChannel(), Server::LowPriority);
606             break;
607         case Konversation::Join:
608             m_server->queue("JOIN " + getTextView()->currentChannel());
609             break;
610         case Konversation::Ping:
611         {
612             unsigned int time_t = QDateTime::currentDateTime().toTime_t();
613             pattern=QString(cc+"CTCP %u PING %1").arg(time_t);
614         }
615         break;
616         case Konversation::Kick:
617             pattern=cc+"KICK %u";
618             break;
619         case Konversation::KickBan:
620             pattern=cc+"BAN %u\n"+
621                 cc+"KICK %u";
622             break;
623         case Konversation::BanNick:
624             pattern=cc+"BAN %u";
625             break;
626         case Konversation::BanHost:
627             pattern=cc+"BAN -HOST %u";
628             break;
629         case Konversation::BanDomain:
630             pattern=cc+"BAN -DOMAIN %u";
631             break;
632         case Konversation::BanUserHost:
633             pattern=cc+"BAN -USERHOST %u";
634             break;
635         case Konversation::BanUserDomain:
636             pattern=cc+"BAN -USERDOMAIN %u";
637             break;
638         case Konversation::KickBanHost:
639             pattern=cc+"KICKBAN -HOST %u";
640             break;
641         case Konversation::KickBanDomain:
642             pattern=cc+"KICKBAN -DOMAIN %u";
643             break;
644         case Konversation::KickBanUserHost:
645             pattern=cc+"KICKBAN -USERHOST %u";
646             break;
647         case Konversation::KickBanUserDomain:
648             pattern=cc+"KICKBAN -USERDOMAIN %u";
649             break;
650         case Konversation::OpenQuery:
651             pattern=cc+"QUERY %u";
652             break;
653         case Konversation::StartDccChat:
654             pattern=cc+"DCC CHAT %u";
655             break;
656         case Konversation::StartDccWhiteboard:
657             pattern=cc+"DCC WHITEBOARD %u";
658             break;
659         case Konversation::DccSend:
660             pattern=cc+"DCC SEND %u";
661             break;
662         case Konversation::IgnoreNick:
663             if (nickList.size() == 1)
664                 question=i18n("Do you want to ignore %1?", nickList.first());
665             else
666                 question = i18n("Do you want to ignore the selected users?");
667             if (KMessageBox::warningContinueCancel(
668                 this,
669                 question,
670                 i18n("Ignore"),
671                 KGuiItem(i18n("Ignore")),
672                 KStandardGuiItem::cancel(),
673                 "IgnoreNick"
674                 ) ==
675                 KMessageBox::Continue)
676                 pattern = cc+"IGNORE -ALL %l";
677             break;
678         case Konversation::UnignoreNick:
679         {
680             QStringList selectedIgnoredNicks;
681
682             for (QStringList::Iterator it=nickList.begin(); it!=nickList.end(); ++it)
683             {
684                 if (Preferences::isIgnored((*it)))
685                     selectedIgnoredNicks.append((*it));
686             }
687
688             if (selectedIgnoredNicks.count() == 1)
689                 question=i18n("Do you want to stop ignoring %1?", selectedIgnoredNicks.first());
690             else
691                 question = i18n("Do you want to stop ignoring the selected users?");
692             if (KMessageBox::warningContinueCancel(
693                 this,
694                 question,
695                 i18n("Unignore"),
696                 KGuiItem(i18n("Unignore")),
697                 KStandardGuiItem::cancel(),
698                 "UnignoreNick") ==
699                 KMessageBox::Continue)
700             {
701                 sendChannelText(cc+"UNIGNORE "+selectedIgnoredNicks.join(" "));
702             }
703             break;
704         }
705         case Konversation::AddNotify:
706         {
707             if (m_server->getServerGroup())
708             {
709                 for (QStringList::Iterator it=nickList.begin(); it!=nickList.end(); ++it)
710                 {
711                     if (!Preferences::isNotify(m_server->getServerGroup()->id(), (*it)))
712                         Preferences::addNotify(m_server->getServerGroup()->id(), (*it));
713                 }
714             }
715             break;
716         }
717     } // switch
718
719     if (!pattern.isEmpty())
720     {
721         pattern.replace("%c",getName());
722
723         QString command;
724
725         if (pattern.contains("%l"))
726         {
727             QStringList list, partialList;
728             int modesCount = m_server->getModesCount();
729
730             for (QStringList::Iterator it=nickList.begin(); it!=nickList.end(); ++it)
731                 list.append((*it));
732
733             for (int index = 0; index<list.count(); index+=modesCount)
734             {
735                 command = pattern;
736                 partialList = list.mid(index, modesCount);
737                 command = command.replace("%l", partialList.join(" "));
738                 const QString repeatedMode = mode.repeated(partialList.count());
739
740                 command = command.replace("%m", repeatedMode);
741                 if (raw)
742                     m_server->queue(command);
743                 else
744                     sendChannelText(command);
745             }
746
747         }
748         else
749         {
750             QStringList patternList = pattern.split('\n', QString::SkipEmptyParts);
751
752             for (QStringList::Iterator it=nickList.begin(); it!=nickList.end(); ++it)
753             {
754                 for (int index = 0; index<patternList.count(); index++)
755                 {
756                     command = patternList[index];
757                     command.replace("%u", (*it));
758
759                     if (raw)
760                         m_server->queue(command);
761                     else
762                         sendChannelText(command);
763                 }
764             }
765         }
766     }
767 }
768
769 // Will be connected to NickListView::doubleClicked()
770 void Channel::doubleClickCommand(QTreeWidgetItem *item, int column)
771 {
772     Q_UNUSED(column)
773     if(item)
774     {
775         nicknameListView->clearSelection();
776         item->setSelected(true);
777         // TODO: put the quick button code in another function to make reusal more legitimate
778         quickButtonClicked(Preferences::self()->channelDoubleClickAction());
779     }
780 }
781
782 void Channel::completeNick()
783 {
784     int pos, oldPos;
785     QTextCursor cursor = channelInput->textCursor();
786
787     pos = cursor.position();
788     oldPos = channelInput->getOldCursorPosition();
789
790     QString line=channelInput->toPlainText();
791     QString newLine;
792     // Check if completion position is out of range
793     if(completionPosition >= nicknameList.count()) completionPosition = 0;
794
795     // Check, which completion mode is active
796     char mode = channelInput->getCompletionMode();
797
798     if(mode == 'c')
799     {
800         line.remove(oldPos, pos - oldPos);
801         pos = oldPos;
802     }
803
804     // If the cursor is at beginning of line, insert last completion
805     if(pos == 0 && !channelInput->lastCompletion().isEmpty())
806     {
807         QString addStart(Preferences::self()->nickCompleteSuffixStart());
808         newLine = channelInput->lastCompletion() + addStart;
809         // New cursor position is behind nickname
810         pos = newLine.length();
811         // Add rest of the line
812         newLine += line;
813     }
814     else
815     {
816         // remember old cursor position in input field
817         channelInput->setOldCursorPosition(pos);
818         // remember old cursor position locally
819         oldPos = pos;
820         // step back to last space or start of line
821         while(pos && line[pos-1] != ' ') pos--;
822         // copy search pattern (lowercase)
823         QString pattern = line.mid(pos, oldPos - pos);
824         // copy line to newLine-buffer
825         newLine = line;
826
827         // did we find any pattern?
828         if(!pattern.isEmpty())
829         {
830             bool complete = false;
831             QString foundNick;
832
833             // try to find matching nickname in list of names
834             if(Preferences::self()->nickCompletionMode() == 1 ||
835                 Preferences::self()->nickCompletionMode() == 2)
836             { // Shell like completion
837                 QStringList found;
838                 foundNick = nicknameList.completeNick(pattern, complete, found,
839                                                       (Preferences::self()->nickCompletionMode() == 2),
840                                                       Preferences::self()->nickCompletionCaseSensitive());
841
842                 if(!complete && !found.isEmpty())
843                 {
844                     if(Preferences::self()->nickCompletionMode() == 1)
845                     {
846                         QString nicksFound = found.join(" ");
847                         appendServerMessage(i18n("Completion"), i18n("Possible completions: %1.", nicksFound));
848                     }
849                     else
850                     {
851                         channelInput->showCompletionList(found);
852                     }
853                 }
854             } // Cycle completion
855             else if(Preferences::self()->nickCompletionMode() == 0 && !nicknameList.isEmpty())
856             {
857                 if(mode == '\0') {
858                     uint timeStamp = 0;
859                     int listPosition = 0;
860
861                     foreach (Nick* nick, nicknameList)
862                     {
863                         if(nick->getChannelNick()->getNickname().startsWith(pattern, Preferences::self()->nickCompletionCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive) &&
864                           (nick->getChannelNick()->timeStamp() > timeStamp))
865                         {
866                             timeStamp = nick->getChannelNick()->timeStamp();
867                             completionPosition = listPosition;
868                         }
869                         ++listPosition;
870                     }
871                 }
872
873                 // remember old nick completion position
874                 int oldCompletionPosition = completionPosition;
875                 complete = true;
876                 QString prefixCharacter = Preferences::self()->prefixCharacter();
877
878                 do
879                 {
880                     QString lookNick = nicknameList.at(completionPosition)->getChannelNick()->getNickname();
881
882                     if(!prefixCharacter.isEmpty() && lookNick.contains(prefixCharacter))
883                     {
884                         lookNick = lookNick.section( prefixCharacter,1 );
885                     }
886
887                     if(lookNick.startsWith(pattern, Preferences::self()->nickCompletionCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive))
888                     {
889                         foundNick = lookNick;
890                     }
891
892                     // increment search position
893                     completionPosition++;
894
895                     // wrap around
896                     if(completionPosition == nicknameList.count())
897                     {
898                         completionPosition = 0;
899                     }
900
901                     // the search ends when we either find a suitable nick or we end up at the
902                     // first search position
903                 } while((completionPosition != oldCompletionPosition) && foundNick.isEmpty());
904             }
905
906             // did we find a suitable nick?
907             if(!foundNick.isEmpty())
908             {
909                 // set channel nicks completion mode
910                 channelInput->setCompletionMode('c');
911
912                 // remove pattern from line
913                 newLine.remove(pos, pattern.length());
914
915                 // did we find the nick in the middle of the line?
916                 if(pos && complete)
917                 {
918                     channelInput->setLastCompletion(foundNick);
919                     QString addMiddle = Preferences::self()->nickCompleteSuffixMiddle();
920                     newLine.insert(pos, foundNick + addMiddle);
921                     pos = pos + foundNick.length() + addMiddle.length();
922                 }
923                 // no, it was at the beginning
924                 else if(complete)
925                 {
926                     channelInput->setLastCompletion(foundNick);
927                     QString addStart = Preferences::self()->nickCompleteSuffixStart();
928                     newLine.insert(pos, foundNick + addStart);
929                     pos = pos + foundNick.length() + addStart.length();
930                 }
931                 // the nick wasn't complete
932                 else
933                 {
934                     newLine.insert(pos, foundNick);
935                     pos = pos + foundNick.length();
936                 }
937             }
938             // no pattern found, so restore old cursor position
939             else pos = oldPos;
940         }
941     }
942
943     // Set new text and cursor position
944     channelInput->setText(newLine);
945     cursor.setPosition(pos);
946     channelInput->setTextCursor(cursor);
947 }
948
949 // make sure to step back one position when completion ends so the user starts
950 // with the last complete they made
951 void Channel::endCompleteNick()
952 {
953     if(completionPosition) completionPosition--;
954     else completionPosition=nicknameList.count()-1;
955 }
956
957 void Channel::setName(const QString& newName)
958 {
959     ChatWindow::setName(newName);
960     setLogfileName(newName.toLower());
961 }
962
963 bool Channel::autoJoin()
964 {
965     if (!m_server->getServerGroup()) return false;
966
967     Konversation::ChannelList channelList = m_server->getServerGroup()->channelList();
968
969     return channelList.contains(channelSettings());
970 }
971
972 void Channel::setAutoJoin(bool autojoin)
973 {
974     if (autojoin && !(autoJoin()))
975     {
976         Konversation::ChannelSettings before;
977
978         QList<Channel *> channelList = m_server->getChannelList();
979
980         if (channelList.count() > 1)
981         {
982             QMap<int, Channel*> channelMap;
983
984             int index = -1;
985             int ownIndex = m_server->getViewContainer()->getViewIndex(this);
986
987             foreach (Channel* channel, channelList)
988             {
989                 index = m_server->getViewContainer()->getViewIndex(channel);
990
991                 if (index && index > ownIndex) channelMap.insert(index, channel);
992             }
993
994             if (channelMap.count())
995             {
996                 QMap<int, Channel*>::Iterator it2;
997                 Channel* channel;
998
999                 for (it2 = channelMap.begin(); it2 != channelMap.end(); ++it2)
1000                 {
1001                     channel = it2.value();
1002
1003                     if (channel->autoJoin())
1004                     {
1005                         before = channel->channelSettings();
1006
1007                         break;
1008                     }
1009                 }
1010             }
1011         }
1012
1013         if (m_server->getServerGroup())
1014             m_server->getServerGroup()->addChannel(channelSettings(), before);
1015     }
1016     else
1017     {
1018         if (m_server->getServerGroup())
1019             m_server->getServerGroup()->removeChannel(channelSettings());
1020     }
1021 }
1022
1023
1024
1025 QString Channel::getPassword()
1026 {
1027     QString password;
1028
1029     for (QStringList::const_iterator it = m_modeList.constBegin(); it != m_modeList.constEnd(); ++it)
1030     {
1031         if ((*it)[0] == 'k') password = (*it).mid(1);
1032     }
1033
1034     if (password.isEmpty() && m_server->getServerGroup())
1035     {
1036         Konversation::ChannelList channelSettingsList = m_server->getServerGroup()->channelList();
1037         Konversation::ChannelSettings channelSettings(getName());
1038         int index = channelSettingsList.indexOf(channelSettings);
1039         if(index >= 0)
1040            password = channelSettingsList.at(index).password();
1041     }
1042
1043     return password;
1044 }
1045
1046 const Konversation::ChannelSettings Channel::channelSettings()
1047 {
1048     Konversation::ChannelSettings channel;
1049
1050     channel.setName(getName());
1051     channel.setPassword(getPassword());
1052     channel.setNotificationsEnabled(notificationsEnabled());
1053
1054     return channel;
1055 }
1056
1057 void Channel::sendFileMenu()
1058 {
1059     emit sendFile();
1060 }
1061
1062 void Channel::channelTextEntered()
1063 {
1064     QString line = channelInput->toPlainText();
1065     channelInput->clear();
1066
1067     if(line.toLower().trimmed() == Preferences::self()->commandChar()+"clear")
1068     {
1069         textView->clear();
1070     }
1071     else if(line.toLower().trimmed() == Preferences::self()->commandChar()+"cycle")
1072     {
1073         cycleChannel();
1074     }
1075     else
1076     {
1077         if(!line.isEmpty())
1078             sendChannelText(sterilizeUnicode(line));
1079     }
1080 }
1081
1082 void Channel::channelPassthroughCommand()
1083 {
1084     QString commandChar = Preferences::self()->commandChar();
1085     QString line = channelInput->toPlainText();
1086
1087     channelInput->clear();
1088
1089     if(!line.isEmpty())
1090     {
1091         // Prepend commandChar on Ctrl+Enter to bypass outputfilter command recognition
1092         if (line.startsWith(commandChar))
1093         {
1094             line = commandChar + line;
1095         }
1096         sendChannelText(sterilizeUnicode(line));
1097     }
1098 }
1099
1100 void Channel::sendChannelText(const QString& sendLine)
1101 {
1102     // create a work copy
1103     QString outputAll(sendLine);
1104     // replace aliases and wildcards
1105     if(m_server->getOutputFilter()->replaceAliases(outputAll))
1106     {
1107         outputAll = m_server->parseWildcards(outputAll,m_server->getNickname(),getName(),getPassword(),
1108             getSelectedNickList(),QString());
1109     }
1110
1111     // Send all strings, one after another
1112     QStringList outList = outputAll.split(QRegExp("[\r\n]+"), QString::SkipEmptyParts);
1113     for(int index=0;index<outList.count();index++)
1114     {
1115         QString output(outList[index]);
1116
1117         // encoding stuff is done in Server()
1118         Konversation::OutputFilterResult result = m_server->getOutputFilter()->parse(m_server->getNickname(),output,getName());
1119
1120         // Is there something we need to display for ourselves?
1121         if(!result.output.isEmpty())
1122         {
1123             if(result.type == Konversation::Action) appendAction(m_server->getNickname(), result.output);
1124             else if(result.type == Konversation::Command) appendCommandMessage(result.typeString, result.output);
1125             else if(result.type == Konversation::Program) appendServerMessage(result.typeString, result.output);
1126             else if(result.type == Konversation::PrivateMessage) msgHelper(result.typeString, result.output);
1127             else append(m_server->getNickname(), result.output);
1128         }
1129         else if (result.outputList.count())
1130         {
1131             if (result.type == Konversation::Message)
1132             {
1133                 QStringListIterator it(result.outputList);
1134
1135                 while (it.hasNext())
1136                     append(m_server->getNickname(), it.next());
1137             }
1138             else if (result.type == Konversation::Action)
1139             {
1140                 for (int i = 0; i < result.outputList.count(); ++i)
1141                 {
1142                     if (i == 0)
1143                         appendAction(m_server->getNickname(), result.outputList.at(i));
1144                     else
1145                         append(m_server->getNickname(), result.outputList.at(i));
1146                 }
1147             }
1148         }
1149
1150         // Send anything else to the server
1151         if (!result.toServerList.empty())
1152             m_server->queueList(result.toServerList);
1153         else
1154             m_server->queue(result.toServer);
1155     }
1156 }
1157
1158 void Channel::setNickname(const QString& newNickname)
1159 {
1160     nicknameCombobox->setCurrentIndex(nicknameCombobox->findText(newNickname));
1161 }
1162
1163 QStringList Channel::getSelectedNickList()
1164 {
1165     QStringList result;
1166
1167     if (channelCommand)
1168         result.append(textView->getContextNick());
1169     else
1170     {
1171         foreach (Nick* nick, nicknameList)
1172         {
1173             if(nick->isSelected()) result.append(nick->getChannelNick()->getNickname());
1174         }
1175     }
1176
1177     return result;
1178 }
1179
1180 ChannelNickList Channel::getSelectedChannelNicks()
1181 {
1182     ChannelNickList result;
1183
1184     foreach (Nick* nick, nicknameList)
1185     {
1186         if(channelCommand)
1187         {
1188             if(nick->getChannelNick()->getNickname() == textView->getContextNick())
1189             {
1190                 result.append(nick->getChannelNick());
1191                 return result;
1192             }
1193         }
1194         else if(nick->isSelected())
1195             result.append(nick->getChannelNick());
1196     }
1197
1198     return result;
1199
1200 }
1201
1202 void Channel::channelLimitChanged()
1203 {
1204     unsigned int lim=limit->text().toUInt();
1205
1206     modeButtonClicked(7,lim>0);
1207 }
1208
1209 void Channel::modeButtonClicked(int id, bool on)
1210 {
1211     char mode[]={'t','n','s','i','p','m','k','l'};
1212     QString command("MODE %1 %2%3 %4");
1213     QString args = getPassword();
1214
1215     if (mode[id] == 'k')
1216     {
1217         if (args.isEmpty())
1218         {
1219             QPointer<KPasswordDialog> dlg = new KPasswordDialog(this);
1220             dlg->setPrompt(i18n("Channel Password"));
1221             if (dlg->exec() && !dlg->password().isEmpty())
1222             {
1223                 args = dlg->password();
1224             }
1225             delete dlg;
1226         }
1227
1228     }
1229     else if(mode[id]=='l')
1230     {
1231         if(limit->text().isEmpty() && on)
1232         {
1233             bool ok=false;
1234             // ask user how many nicks should be the limit
1235             args=KInputDialog::getText(i18n("Channel User Limit"),
1236                 i18n("Enter the new user limit for the channel:"),
1237                 limit->text(),                    // will be always "" but what the hell ;)
1238                 &ok,
1239                 this);
1240             // leave this function if user cancels
1241             if(!ok) return;
1242         }
1243         else if(on)
1244             args=limit->text();
1245     }
1246     // put together the mode command and send it to the server queue
1247     m_server->queue(command.arg(getName()).arg((on) ? "+" : "-").arg(mode[id]).arg(args));
1248 }
1249
1250 void Channel::quickButtonClicked(const QString &buttonText)
1251 {
1252     // parse wildcards (toParse,nickname,channelName,nickList,queryName,parameter)
1253     QString out=m_server->parseWildcards(buttonText,m_server->getNickname(),getName(),getPassword(),getSelectedNickList(),QString());
1254
1255     // are there any newlines in the definition?
1256     if (out.contains('\n'))
1257         sendChannelText(out);
1258     // single line without newline needs to be copied into input line
1259     else
1260         channelInput->setText(out);
1261 }
1262
1263 void Channel::addNickname(ChannelNickPtr channelnick)
1264 {
1265     QString nickname = channelnick->loweredNickname();
1266
1267     Nick* nick=0;
1268
1269     foreach (Nick* lookNick, nicknameList)
1270     {
1271         if(lookNick->getChannelNick()->loweredNickname() == nickname)
1272         {
1273             nick = lookNick;
1274             break;
1275         }
1276     }
1277
1278     if (nick == 0)
1279     {
1280         fastAddNickname(channelnick);
1281
1282         if(channelnick->isAnyTypeOfOp())
1283         {
1284             adjustOps(1);
1285         }
1286
1287         adjustNicks(1);
1288         requestNickListSort();
1289     }
1290     else
1291         kWarning() << "Nickname " << channelnick->getNickname() << " has not been added as it is already in the nickname list."<< endl;
1292 }
1293
1294 // Use with caution! Does not check for duplicates or may not
1295 // sort if delayed sorting is in effect.
1296 void Channel::fastAddNickname(ChannelNickPtr channelnick, Nick *nick)
1297 {
1298     Q_ASSERT(channelnick);
1299     if(!channelnick) return;
1300
1301     if (!nick || !nick->treeWidget())
1302     {
1303         // Deal with nicknameListView now (creating nick if necessary)
1304         NickListView::NoSorting noSorting(nicknameListView);
1305         int index = nicknameListView->topLevelItemCount();
1306
1307         // Append nick to the lists
1308         if (nick)
1309         {
1310             nicknameListView->addTopLevelItem(nick);
1311         }
1312         else
1313         {
1314             nick = new Nick(nicknameListView, this, channelnick);
1315             m_nicknameListViewTextChanged |= 0xFF; // new nick, text changed.
1316         }
1317
1318         if (!m_delayedSortTimer->isActive()) {
1319             // Find its right place and insert where it belongs
1320             int newindex = nicknameListView->findLowerBound(*nick);
1321             if (newindex != index) {
1322                 if (newindex >= index)
1323                     newindex--;
1324                 nicknameListView->takeTopLevelItem(index);
1325                 nicknameListView->insertTopLevelItem(newindex, nick);
1326             }
1327         }
1328         // Otherwise it will be sorted by delayed sort.
1329     }
1330
1331     // Now deal with nicknameList
1332     if (m_delayedSortTimer->isActive())
1333     {
1334         // nicks get sorted later
1335         nicknameList.append(nick);
1336     } else {
1337         NickList::iterator it = qLowerBound(nicknameList.begin(), nicknameList.end(), nick, nickLessThan);
1338         nicknameList.insert(it, nick);
1339     }
1340
1341 }
1342
1343 void Channel::nickRenamed(const QString &oldNick, const NickInfo& nickInfo)
1344 {
1345
1346     /* Did we change our nick name? */
1347     QString newNick = nickInfo.getNickname();
1348
1349     if(newNick == m_server->getNickname()) /* Check newNick because  m_server->getNickname() is already updated to new nick */
1350     {
1351         setNickname(newNick);
1352         appendCommandMessage(i18n("Nick"),i18n("You are now known as %1.", newNick), false, true, true);
1353     }
1354     else
1355     {
1356         /* No, must've been someone else */
1357         appendCommandMessage(i18n("Nick"),i18n("%1 is now known as %2.", oldNick, newNick),false);
1358     }
1359
1360     Nick *nick = getNickByName(newNick);
1361     if (nick)
1362     {
1363         repositionNick(nick);
1364     }
1365 }
1366
1367 void Channel::joinNickname(ChannelNickPtr channelNick)
1368 {
1369     if(channelNick->getNickname() == m_server->getNickname())
1370     {
1371         m_joined = true;
1372         emit joined(this);
1373         appendCommandMessage(i18n("Join"), i18nc("%1 is the channel and %2 is our hostmask",
1374                              "You have joined the channel %1 (%2).", getName(), channelNick->getHostmask()),false, false, true);
1375         m_ownChannelNick = channelNick;
1376         refreshModeButtons();
1377         setActive(true);
1378
1379         //HACK the way the notification priorities work sucks, this forces the tab text color to ungray right now.
1380         if (m_currentTabNotify == Konversation::tnfNone || (!Preferences::self()->tabNotificationsEvents() && m_currentTabNotify == Konversation::tnfControl))
1381             Application::instance()->getMainWindow()->getViewContainer()->unsetViewNotification(this);
1382
1383         Application::instance()->notificationHandler()->channelJoin(this,getName());
1384     }
1385     else
1386     {
1387         QString nick = channelNick->getNickname();
1388         QString hostname = channelNick->getHostmask();
1389         appendCommandMessage(i18n("Join"), i18nc("%1 is the nick joining and %2 the hostmask of that nick",
1390                              "%1 has joined this channel (%2).", nick, hostname),false, false);
1391         addNickname(channelNick);
1392     }
1393 }
1394
1395 void Channel::removeNick(ChannelNickPtr channelNick, const QString &reason, bool quit)
1396 {
1397     QString displayReason = reason;
1398
1399     if(!displayReason.isEmpty())
1400     {
1401         // if the reason contains text markup characters, play it safe and reset all
1402         if(displayReason.contains(QRegExp("[\\0000-\\0037]")))
1403             displayReason += "\017";
1404     }
1405
1406     if(channelNick->getNickname() == m_server->getNickname())
1407     {
1408         //If in the future we can leave a channel, but not close the window, refreshModeButtons() has to be called.
1409         if (quit)
1410         {
1411             if (displayReason.isEmpty())
1412                 appendCommandMessage(i18n("Quit"), i18n("You have left this server."), false);
1413             else
1414                 appendCommandMessage(i18n("Quit"), i18nc("%1 adds the reason", "You have left this server (%1).", displayReason), false);
1415         }
1416         else
1417         {
1418             if (displayReason.isEmpty())
1419                 appendCommandMessage(i18n("Part"), i18n("You have left channel %1.", getName()), false);
1420             else
1421                 appendCommandMessage(i18n("Part"), i18nc("%1 adds the channel and %2 the reason",
1422                                      "You have left channel %1 (%2).", getName(), displayReason), false);
1423
1424         }
1425
1426         delete this;
1427     }
1428     else
1429     {
1430         if (quit)
1431         {
1432             if (displayReason.isEmpty())
1433                 appendCommandMessage(i18n("Quit"), i18n("%1 has left this server.", channelNick->getNickname()), false);
1434             else
1435                 appendCommandMessage(i18n("Quit"), i18nc("%1 adds the nick and %2 the reason",
1436                                      "%1 has left this server (%2).", channelNick->getNickname(), displayReason), false);
1437         }
1438         else
1439         {
1440             if (displayReason.isEmpty())
1441                 appendCommandMessage(i18n("Part"), i18n("%1 has left this channel.", channelNick->getNickname()), false);
1442             else
1443                 appendCommandMessage(i18n("Part"), i18nc("%1 adds the nick and %2 the reason",
1444                                      "%1 has left this channel (%2).", channelNick->getNickname(), displayReason), false);
1445         }
1446
1447         if(channelNick->isAnyTypeOfOp())
1448         {
1449             adjustOps(-1);
1450         }
1451
1452         adjustNicks(-1);
1453         Nick* nick = getNickByName(channelNick->getNickname());
1454
1455         if(nick)
1456         {
1457             nicknameList.removeOne(nick);
1458             delete nick;
1459             // Execute this otherwise it may crash trying to access deleted nick
1460             nicknameListView->executeDelayedItemsLayout();
1461         }
1462         else
1463         {
1464             kWarning() << "Nickname " << channelNick->getNickname() << " not found!"<< endl;
1465         }
1466     }
1467 }
1468
1469 void Channel::flushPendingNicks()
1470 {
1471     if (m_processingTimer)
1472     {
1473         m_processingTimer->stop();
1474     }
1475
1476     while (!m_pendingChannelNickLists.isEmpty())
1477     {
1478         processPendingNicks();
1479     }
1480 }
1481
1482 void Channel::kickNick(ChannelNickPtr channelNick, const QString &kicker, const QString &reason)
1483 {
1484     QString displayReason = reason;
1485
1486     if(!displayReason.isEmpty())
1487     {
1488         // if the reason contains text markup characters, play it safe and reset all
1489         if(displayReason.contains(QRegExp("[\\0000-\\0037]")))
1490             displayReason += "\017";
1491     }
1492
1493     if(channelNick->getNickname() == m_server->getNickname())
1494     {
1495         if(kicker == m_server->getNickname())
1496         {
1497             if (displayReason.isEmpty())
1498                 appendCommandMessage(i18n("Kick"), i18n("You have kicked yourself from channel %1.", getName()));
1499             else
1500                 appendCommandMessage(i18n("Kick"), i18nc("%1 adds the channel and %2 the reason",
1501                                               "You have kicked yourself from channel %1 (%2).", getName(), displayReason));
1502         }
1503         else
1504         {
1505             if (displayReason.isEmpty())
1506             {
1507                 appendCommandMessage(i18n("Kick"), i18nc("%1 adds the channel, %2 adds the kicker",
1508                                               "You have been kicked from channel %1 by %2.", getName(), kicker), true);
1509             }
1510             else
1511             {
1512                 appendCommandMessage(i18n("Kick"), i18nc("%1 adds the channel, %2 the kicker and %3 the reason",
1513                                               "You have been kicked from channel %1 by %2 (%3).", getName(), kicker, displayReason), true);
1514             }
1515
1516             Application::instance()->notificationHandler()->kick(this,getName(), kicker);
1517         }
1518
1519         m_joined=false;
1520         setActive(false);
1521
1522         //HACK the way the notification priorities work sucks, this forces the tab text color to gray right now.
1523         if (m_currentTabNotify == Konversation::tnfNone || (!Preferences::self()->tabNotificationsEvents() && m_currentTabNotify == Konversation::tnfControl))
1524             Application::instance()->getMainWindow()->getViewContainer()->unsetViewNotification(this);
1525
1526         return;
1527     }
1528     else
1529     {
1530         if(kicker == m_server->getNickname())
1531         {
1532             if (displayReason.isEmpty())
1533                 appendCommandMessage(i18n("Kick"), i18n("You have kicked %1 from the channel.", channelNick->getNickname()));
1534             else
1535                 appendCommandMessage(i18n("Kick"), i18nc("%1 adds the kicked nick and %2 the reason",
1536                                      "You have kicked %1 from the channel (%2).", channelNick->getNickname(), displayReason), true);
1537         }
1538         else
1539         {
1540             if (displayReason.isEmpty())
1541             {
1542                 appendCommandMessage(i18n("Kick"), i18nc("%1 adds the kicked nick, %2 adds the kicker",
1543                                      "%1 has been kicked from the channel by %2.", channelNick->getNickname(), kicker), true);
1544             }
1545             else
1546             {
1547                 appendCommandMessage(i18n("Kick"), i18nc("%1 adds the kicked nick, %2 the kicker and %3 the reason",
1548                                      "%1 has been kicked from the channel by %2 (%3).", channelNick->getNickname(), kicker, displayReason), true);
1549             }
1550         }
1551
1552         if(channelNick->isAnyTypeOfOp())
1553             adjustOps(-1);
1554
1555         adjustNicks(-1);
1556         Nick* nick = getNickByName(channelNick->getNickname());
1557
1558         if(nick == 0)
1559         {
1560             kWarning() << "Nickname " << channelNick->getNickname() << " not found!"<< endl;
1561         }
1562         else
1563         {
1564             nicknameList.removeOne(nick);
1565             delete nick;
1566         }
1567     }
1568 }
1569
1570 Nick* Channel::getNickByName(const QString &lookname)
1571 {
1572     QString lcLookname(lookname.toLower());
1573
1574     foreach (Nick* nick, nicknameList)
1575     {
1576         if(nick->getChannelNick()->loweredNickname() == lcLookname)
1577             return nick;
1578     }
1579
1580     return 0;
1581 }
1582
1583 void Channel::adjustNicks(int value)
1584 {
1585     if((nicks == 0) && (value <= 0))
1586     {
1587         return;
1588     }
1589
1590     nicks += value;
1591
1592     if(nicks < 0)
1593     {
1594         nicks = 0;
1595     }
1596
1597     emitUpdateInfo();
1598 }
1599
1600 void Channel::adjustOps(int value)
1601 {
1602     if((ops == 0) && (value <= 0))
1603     {
1604         return;
1605     }
1606
1607     ops += value;
1608
1609     if(ops < 0)
1610     {
1611         ops = 0;
1612     }
1613
1614     emitUpdateInfo();
1615 }
1616
1617 void Channel::emitUpdateInfo()
1618 {
1619     QString info = getName() + " - ";
1620     info += i18np("%1 nick", "%1 nicks", numberOfNicks());
1621     info += i18np(" (%1 op)", " (%1 ops)", numberOfOps());
1622
1623     emit updateInfo(info);
1624 }
1625
1626 void Channel::setTopic(const QString &newTopic)
1627 {
1628     appendCommandMessage(i18n("Topic"), i18n("The channel topic is \"%1\".", newTopic));
1629     QString topic = Konversation::removeIrcMarkup(newTopic);
1630     topicLine->setText(topic);
1631     topicAuthorUnknown=true; // if we only get called with a topic, it was a 332, which usually has a 333 next
1632
1633     // cut off "nickname" and "time_t" portion of the topic before comparing, otherwise the history
1634     // list will fill up with the same entries while the user only requests the topic to be seen.
1635
1636     if(m_topicHistory.isEmpty() || (m_topicHistory.first().section(' ', 2) != newTopic))
1637     {
1638         m_topicHistory.prepend(QString("%1 "+i18n("unknown")+" %2").arg(QDateTime::currentDateTime().toTime_t()).arg(newTopic));
1639         emit topicHistoryChanged();
1640     }
1641 }
1642
1643 void Channel::setTopic(const QString &nickname, const QString &newTopic) // Overloaded
1644 {
1645     if(nickname == m_server->getNickname())
1646     {
1647         appendCommandMessage(i18n("Topic"), i18n("You set the channel topic to \"%1\".", newTopic));
1648     }
1649     else
1650     {
1651         appendCommandMessage(i18n("Topic"), i18n("%1 sets the channel topic to \"%2\".", nickname, newTopic));
1652     }
1653
1654     m_topicHistory.prepend(QString("%1 %2 %3").arg(QDateTime::currentDateTime().toTime_t()).arg(nickname).arg(newTopic));
1655     QString topic = Konversation::removeIrcMarkup(newTopic);
1656     topicLine->setText(topic);
1657
1658     emit topicHistoryChanged();
1659 }
1660
1661 QStringList Channel::getTopicHistory()
1662 {
1663     return m_topicHistory;
1664 }
1665
1666 QString Channel::getTopic()
1667 {
1668     return m_topicHistory[0];
1669 }
1670
1671 void Channel::setTopicAuthor(const QString& newAuthor, QDateTime time)
1672 {
1673     if (time.isNull() || !time.isValid())
1674         time=QDateTime::currentDateTime();
1675
1676     if(topicAuthorUnknown && !m_topicHistory.isEmpty())
1677     {
1678         m_topicHistory[0] =  QString("%1").arg(time.toTime_t()) + ' ' + newAuthor + ' ' + m_topicHistory[0].section(' ', 2);
1679         topicAuthorUnknown = false;
1680
1681         emit topicHistoryChanged();
1682     }
1683 }
1684
1685 void Channel::updateMode(const QString& sourceNick, char mode, bool plus, const QString &parameter)
1686 {
1687     //Note for future expansion: doing m_server->getChannelNick(getName(), sourceNick);  may not return a valid channelNickPtr if the
1688     //mode is updated by the server.
1689
1690     // Note: nick repositioning in the nicknameListView should be triggered by
1691     // nickinfo / channelnick signals
1692
1693     QString message;
1694     ChannelNickPtr parameterChannelNick=m_server->getChannelNick(getName(), parameter);
1695
1696     bool fromMe=false;
1697     bool toMe=false;
1698     bool banTypeThang = m_server->banAddressListModes().contains(QChar(mode));
1699
1700     // HACK to avoid changing strings for 1.2.2, we pretend any TYPE A mode is a
1701     // ban except for e and I, as we have support for those
1702     if (banTypeThang)
1703     {
1704         if (mode != 'b' && mode != 'e' && mode != 'I')
1705             mode = 'b';
1706     }
1707
1708     // remember if this nick had any type of op.
1709     bool wasAnyOp=false;
1710     if (parameterChannelNick)
1711     {
1712         // If NAMES processing is in progress, we likely have received
1713         // a NAMES just prior to the MODE that caused this method to
1714         // be run. If this nick is not yet in the nicklist (e.g. be-
1715         // cause it's just after JOIN and the nicklist is still empty
1716         // prior to the initial NAMES processing), the NAMES process-
1717         // ing can set the ChannelNick's mode data to outdated infor-
1718         // mation. By adding the nickname to the nicklist here if NA-
1719         // MES processing is in progress, we prevent this, as the NA-
1720         // MES processing code will ignore nicks already in the nick-
1721         // list.
1722         // We also add the nickname if the timer hasn't been instanci-
1723         // ated yet, as this means addPendingNickList() has never run
1724         // (yet) and thus NAMES hasn't been processed so far.
1725         if (!m_processingTimer || m_processingTimer->isActive())
1726             addNickname(parameterChannelNick);
1727
1728         wasAnyOp=parameterChannelNick->isAnyTypeOfOp();
1729     }
1730
1731     if(sourceNick.toLower()==m_server->loweredNickname())
1732         fromMe=true;
1733     if(parameter.toLower()==m_server->loweredNickname())
1734         toMe=true;
1735
1736     switch(mode)
1737     {
1738         case 'q':
1739             if(plus)
1740             {
1741                 if(fromMe)
1742                 {
1743                     if(toMe)
1744                         message=i18n("You give channel owner privileges to yourself.");
1745                     else
1746                         message=i18n("You give channel owner privileges to %1.", parameter);
1747                 }
1748                 else
1749                 {
1750                     if(toMe)
1751                         message=i18n("%1 gives channel owner privileges to you.", sourceNick);
1752                     else
1753                         message=i18n("%1 gives channel owner privileges to %2.", sourceNick, parameter);
1754                 }
1755             }
1756             else
1757             {
1758                 if(fromMe)
1759                 {
1760                     if(toMe)
1761                         message=i18n("You take channel owner privileges from yourself.");
1762                     else
1763                         message=i18n("You take channel owner privileges from %1.", parameter);
1764                 }
1765                 else
1766                 {
1767                     if(toMe)
1768                         message=i18n("%1 takes channel owner privileges from you.", sourceNick);
1769                     else
1770                         message=i18n("%1 takes channel owner privileges from %2.", sourceNick, parameter);
1771                 }
1772             }
1773             if(parameterChannelNick)
1774             {
1775                 parameterChannelNick->setOwner(plus);
1776                 emitUpdateInfo();
1777             }
1778             break;
1779
1780         case 'a':
1781             if(plus)
1782             {
1783                 if(fromMe)
1784                 {
1785                     if(toMe)
1786                         message=i18n("You give channel admin privileges to yourself.");
1787                     else
1788                         message=i18n("You give channel admin privileges to %1.", parameter);
1789                 }
1790                 else
1791                 {
1792                     if(toMe)
1793                         message=i18n("%1 gives channel admin privileges to you.", sourceNick);
1794                     else
1795                         message=i18n("%1 gives channel admin privileges to %2.", sourceNick, parameter);
1796                 }
1797             }
1798             else
1799             {
1800                 if(fromMe)
1801                 {
1802                     if(toMe)
1803                         message=i18n("You take channel admin privileges from yourself.");
1804                     else
1805                         message=i18n("You take channel admin privileges from %1.", parameter);
1806                 }
1807                 else
1808                 {
1809                     if(toMe)
1810                         message=i18n("%1 takes channel admin privileges from you.", sourceNick);
1811                     else
1812                         message=i18n("%1 takes channel admin privileges from %2.", sourceNick, parameter);
1813                 }
1814             }
1815             if(parameterChannelNick)
1816             {
1817                 parameterChannelNick->setOwner(plus);
1818                 emitUpdateInfo();
1819             }
1820             break;
1821
1822         case 'o':
1823             if(plus)
1824             {
1825                 if(fromMe)
1826                 {
1827                     if(toMe)
1828                         message=i18n("You give channel operator privileges to yourself.");
1829                     else
1830                         message=i18n("You give channel operator privileges to %1.", parameter);
1831                 }
1832                 else
1833                 {
1834                     if(toMe)
1835                         message=i18n("%1 gives channel operator privileges to you.", sourceNick);
1836                     else
1837                         message=i18n("%1 gives channel operator privileges to %2.", sourceNick, parameter);
1838                 }
1839             }
1840             else
1841             {
1842                 if(fromMe)
1843                 {
1844                     if(toMe)
1845                         message=i18n("You take channel operator privileges from yourself.");
1846                     else
1847                         message=i18n("You take channel operator privileges from %1.", parameter);
1848                 }
1849                 else
1850                 {
1851                     if(toMe)
1852                         message=i18n("%1 takes channel operator privileges from you.", sourceNick);
1853                     else
1854                         message=i18n("%1 takes channel operator privileges from %2.", sourceNick, parameter);
1855                 }
1856             }
1857             if(parameterChannelNick)
1858             {
1859                 parameterChannelNick->setOp(plus);
1860                 emitUpdateInfo();
1861             }
1862             break;
1863
1864         case 'h':
1865             if(plus)
1866             {
1867                 if(fromMe)
1868                 {
1869                     if(toMe)
1870                         message=i18n("You give channel halfop privileges to yourself.");
1871                     else
1872                         message=i18n("You give channel halfop privileges to %1.", parameter);
1873                 }
1874                 else
1875                 {
1876                     if(toMe)
1877                         message=i18n("%1 gives channel halfop privileges to you.", sourceNick);
1878                     else
1879                         message=i18n("%1 gives channel halfop privileges to %2.", sourceNick, parameter);
1880                 }
1881             }
1882             else
1883             {
1884                 if(fromMe)
1885                 {
1886                     if(toMe)
1887                         message=i18n("You take channel halfop privileges from yourself.");
1888                     else
1889                         message=i18n("You take channel halfop privileges from %1.", parameter);
1890                 }
1891                 else
1892                 {
1893                     if(toMe)
1894                         message=i18n("%1 takes channel halfop privileges from you.", sourceNick);
1895                     else
1896                         message=i18n("%1 takes channel halfop privileges from %2.", sourceNick, parameter);
1897                 }
1898             }
1899             if(parameterChannelNick)
1900             {
1901                 parameterChannelNick->setHalfOp(plus);
1902                 emitUpdateInfo();
1903             }
1904             break;
1905
1906         //case 'O': break;
1907
1908         case 'v':
1909             if(plus)
1910             {
1911                 if(fromMe)
1912                 {
1913                     if(toMe) message=i18n("You give yourself permission to talk.");
1914                     else     message=i18n("You give %1 permission to talk.", parameter);
1915                 }
1916                 else
1917                 {
1918                     if(toMe) message=i18n("%1 gives you permission to talk.", sourceNick);
1919                     else     message=i18n("%1 gives %2 permission to talk.", sourceNick, parameter);
1920                 }
1921             }
1922             else
1923             {
1924                 if(fromMe)
1925                 {
1926                     if(toMe) message=i18n("You take the permission to talk from yourself.");
1927                     else     message=i18n("You take the permission to talk from %1.", parameter);
1928                 }
1929                 else
1930                 {
1931                     if(toMe) message=i18n("%1 takes the permission to talk from you.", sourceNick);
1932                     else     message=i18n("%1 takes the permission to talk from %2.", sourceNick, parameter);
1933                 }
1934             }
1935             if(parameterChannelNick)
1936             {
1937                 parameterChannelNick->setVoice(plus);
1938             }
1939             break;
1940
1941         case 'c':
1942             if(plus)
1943             {
1944                 if(fromMe) message=i18n("You set the channel mode to 'no colors allowed'.");
1945                 else message=i18n("%1 sets the channel mode to 'no colors allowed'.", sourceNick);
1946             }
1947             else
1948             {
1949                 if(fromMe) message=i18n("You set the channel mode to 'allow color codes'.");
1950                 else message=i18n("%1 sets the channel mode to 'allow color codes'.", sourceNick);
1951             }
1952             break;
1953
1954         case 'i':
1955             if(plus)
1956             {
1957                 if(fromMe) message=i18n("You set the channel mode to 'invite only'.");
1958                 else message=i18n("%1 sets the channel mode to 'invite only'.", sourceNick);
1959             }
1960             else
1961             {
1962                 if(fromMe) message=i18n("You remove the 'invite only' mode from the channel.");
1963                 else message=i18n("%1 removes the 'invite only' mode from the channel.", sourceNick);
1964             }
1965             modeI->setDown(plus);
1966             break;
1967
1968         case 'm':
1969             if(plus)
1970             {
1971                 if(fromMe) message=i18n("You set the channel mode to 'moderated'.");
1972                 else message=i18n("%1 sets the channel mode to 'moderated'.", sourceNick);
1973             }
1974             else
1975             {
1976                 if(fromMe) message=i18n("You set the channel mode to 'unmoderated'.");
1977                 else message=i18n("%1 sets the channel mode to 'unmoderated'.", sourceNick);
1978             }
1979             modeM->setDown(plus);
1980             break;
1981
1982         case 'n':
1983             if(plus)
1984             {
1985                 if(fromMe) message=i18n("You set the channel mode to 'no messages from outside'.");
1986                 else message=i18n("%1 sets the channel mode to 'no messages from outside'.", sourceNick);
1987             }
1988             else
1989             {
1990                 if(fromMe) message=i18n("You set the channel mode to 'allow messages from outside'.");
1991                 else message=i18n("%1 sets the channel mode to 'allow messages from outside'.", sourceNick);
1992             }
1993             modeN->setDown(plus);
1994             break;
1995
1996         case 'p':
1997             if(plus)
1998             {
1999                 if(fromMe) message=i18n("You set the channel mode to 'private'.");
2000                 else message=i18n("%1 sets the channel mode to 'private'.", sourceNick);
2001             }
2002             else
2003             {
2004                 if(fromMe) message=i18n("You set the channel mode to 'public'.");
2005                 else message=i18n("%1 sets the channel mode to 'public'.", sourceNick);
2006             }
2007             modeP->setDown(plus);
2008             if(plus) modeS->setDown(false);
2009             break;
2010
2011         case 's':
2012             if(plus)
2013             {
2014                 if(fromMe) message=i18n("You set the channel mode to 'secret'.");
2015                 else message=i18n("%1 sets the channel mode to 'secret'.", sourceNick);
2016             }
2017             else
2018             {
2019                 if(fromMe) message=i18n("You set the channel mode to 'visible'.");
2020                 else message=i18n("%1 sets the channel mode to 'visible'.", sourceNick);
2021             }
2022             modeS->setDown(plus);
2023             if(plus) modeP->setDown(false);
2024             break;
2025
2026         //case 'r': break;
2027
2028         case 't':
2029             if(plus)
2030             {
2031                 if(fromMe) message=i18n("You switch on 'topic protection'.");
2032                 else message=i18n("%1 switches on 'topic protection'.", sourceNick);
2033             }
2034             else
2035             {
2036                 if(fromMe) message=i18n("You switch off 'topic protection'.");
2037                 else message=i18n("%1 switches off 'topic protection'.", sourceNick);
2038             }
2039             modeT->setDown(plus);
2040             break;
2041
2042         case 'k':
2043             if(plus)
2044             {
2045                 if(fromMe) message=i18n("You set the channel key to '%1'.", parameter);
2046                 else message=i18n("%1 sets the channel key to '%2'.", sourceNick, parameter);
2047             }
2048             else
2049             {
2050                 if(fromMe) message=i18n("You remove the channel key.");
2051                 else message=i18n("%1 removes the channel key.", sourceNick);
2052             }
2053             modeK->setDown(plus);
2054             break;
2055
2056         case 'l':
2057             if(plus)
2058             {
2059                 if(fromMe) message=i18np("You set the channel limit to 1 nick.", "You set the channel limit to %1 nicks.", parameter);
2060                 else message=i18np("%2 sets the channel limit to 1 nick.", "%2 sets the channel limit to %1 nicks.", parameter, sourceNick);
2061             }
2062             else
2063             {
2064                 if(fromMe) message=i18n("You remove the channel limit.");
2065                 else message=i18n("%1 removes the channel limit.", sourceNick);
2066             }
2067             modeL->setDown(plus);
2068             if(plus) limit->setText(parameter);
2069             else limit->clear();
2070             break;
2071
2072         case 'b':
2073             if(plus)
2074             {
2075                 if(fromMe) message=i18n("You set a ban on %1.", parameter);
2076                 else message=i18n("%1 sets a ban on %2.", sourceNick, parameter);
2077             }
2078             else
2079             {
2080                 if(fromMe) message=i18n("You remove the ban on %1.", parameter);
2081                 else message=i18n("%1 removes the ban on %2.", sourceNick, parameter);
2082             }
2083             break;
2084
2085         case 'e':
2086             if(plus)
2087             {
2088                 if(fromMe) message=i18n("You set a ban exception on %1.", parameter);
2089                 else message=i18n("%1 sets a ban exception on %2.", sourceNick, parameter);
2090             }
2091             else
2092             {
2093                 if(fromMe) message=i18n("You remove the ban exception on %1.", parameter);
2094                 else message=i18n("%1 removes the ban exception on %2.", sourceNick, parameter);
2095             }
2096             break;
2097
2098         case 'I':
2099             if(plus)
2100             {
2101                 if(fromMe) message=i18n("You set invitation mask %1.", parameter);
2102                 else message=i18n("%1 sets invitation mask %2.", sourceNick, parameter);
2103             }
2104             else
2105             {
2106                 if(fromMe) message=i18n("You remove the invitation mask %1.", parameter);
2107                 else message=i18n("%1 removes the invitation mask %2.", sourceNick, parameter);
2108             }
2109             break;
2110         default:
2111         if(plus)
2112         {
2113             if(Konversation::getChannelModesHash().contains(mode))
2114             {
2115                 if (fromMe) message=i18n("You set the channel mode '%1'.", Konversation::getChannelModesHash().value(mode));
2116                 else message= i18n("%1 sets the channel mode '%2'.", sourceNick, Konversation::getChannelModesHash().value(mode));
2117             }
2118             else
2119             {
2120                 if(fromMe) message=i18n("You set channel mode +%1", QString(mode));
2121                         else message=i18n("%1 sets channel mode +%2", sourceNick, QString(mode));
2122             }
2123             }
2124             else
2125         {
2126             if(Konversation::getChannelModesHash().contains(mode))
2127             {
2128                 if (fromMe) message=i18n("You remove the channel mode '%1'.", Konversation::getChannelModesHash().value(mode));
2129                 else message= i18n("%1 removes the channel mode '%2'.", sourceNick, Konversation::getChannelModesHash().value(mode));
2130             }
2131             else
2132             {
2133                     if (fromMe) message=i18n("You set channel mode -%1", QString(mode));
2134                         else message= i18n("%1 sets channel mode -%2", sourceNick, QString(mode));
2135             }
2136             }
2137     }
2138
2139     // check if this nick's anyOp-status has changed and adjust ops accordingly
2140     if(parameterChannelNick)
2141     {
2142         if(wasAnyOp && (!parameterChannelNick->isAnyTypeOfOp()))
2143             adjustOps(-1);
2144         else if((!wasAnyOp) && parameterChannelNick->isAnyTypeOfOp())
2145             adjustOps(1);
2146     }
2147
2148     if(!message.isEmpty() && !Preferences::self()->useLiteralModes())
2149     {
2150         appendCommandMessage(i18n("Mode"),message);
2151     }
2152
2153     updateModeWidgets(mode,plus,parameter);
2154 }
2155
2156 void Channel::clearModeList()
2157 {
2158     QString k;
2159
2160     // Keep channel password in the backing store, for rejoins.
2161     for (QStringList::const_iterator it = m_modeList.constBegin(); it != m_modeList.constEnd(); ++it)
2162     {
2163         if ((*it)[0] == 'k') k = (*it);
2164     }
2165
2166     m_modeList.clear();
2167
2168     if (!k.isEmpty()) m_modeList << k;
2169
2170     modeT->setOn(0);
2171     modeT->setDown(0);
2172
2173     modeN->setOn(0);
2174     modeN->setDown(0);
2175
2176     modeS->setOn(0);
2177     modeS->setDown(0);
2178
2179     modeI->setOn(0);
2180     modeI->setDown(0);
2181
2182     modeP->setOn(0);
2183     modeP->setDown(0);
2184
2185     modeM->setOn(0);
2186     modeM->setDown(0);
2187
2188     modeK->setOn(0);
2189     modeK->setDown(0);
2190
2191     modeL->setOn(0);
2192     modeL->setDown(0);
2193
2194     limit->clear();
2195
2196     emit modesChanged();
2197 }
2198
2199 void Channel::updateModeWidgets(char mode, bool plus, const QString &parameter)
2200 {
2201     ModeButton* widget=0;
2202
2203     if(mode=='t') widget=modeT;
2204     else if(mode=='n') widget=modeN;
2205     else if(mode=='s') widget=modeS;
2206     else if(mode=='i') widget=modeI;
2207     else if(mode=='p') widget=modeP;
2208     else if(mode=='m') widget=modeM;
2209     else if(mode=='k') widget=modeK;
2210     else if(mode=='l')
2211     {
2212         widget=modeL;
2213         if(plus) limit->setText(parameter);
2214         else limit->clear();
2215     }
2216
2217     if(widget) widget->setOn(plus);
2218
2219     if(plus)
2220     {
2221         m_modeList.append(QString(mode + parameter));
2222     }
2223     else
2224     {
2225         QStringList removable = m_modeList.filter(QRegExp(QString("^%1.*").arg(mode)));
2226         foreach(const QString &mode, removable)
2227         {
2228             m_modeList.removeOne(mode);
2229         }
2230     }
2231     emit modesChanged();
2232 }
2233
2234 void Channel::updateQuickButtons(const QStringList &newButtonList)
2235 {
2236     // remove quick buttons from memory and GUI
2237     qDeleteAll(buttonList);
2238     buttonList.clear();
2239
2240     if(m_buttonsGrid) delete m_buttonsGrid;
2241
2242     // the grid that holds the quick action buttons
2243     m_buttonsGrid = new QWidget (nickListButtons); //Q3Grid(2, nickListButtons);
2244     m_buttonsGrid->hide();
2245     QGridLayout* layout = new QGridLayout (m_buttonsGrid);
2246     layout->setMargin(0);
2247
2248     int col = 0;
2249     int row = 0;
2250     // add new quick buttons
2251     for(int index=0;index<newButtonList.count();index++)
2252     {
2253         // generate empty buttons first, text will be added later
2254         QuickButton* quickButton = new QuickButton(QString(), QString(), m_buttonsGrid);
2255         col = index % 2;
2256         layout->addWidget (quickButton, row, col);
2257         row += col;
2258         buttonList.append(quickButton);
2259
2260         connect(quickButton, SIGNAL(clicked(const QString &)), this, SLOT(quickButtonClicked(const QString &)));
2261
2262         // Get the button definition
2263         QString buttonText=newButtonList[index];
2264         // Extract button label
2265         QString buttonLabel=buttonText.section(',',0,0);
2266         // Extract button definition
2267         buttonText=buttonText.section(',',1);
2268
2269         quickButton->setText(buttonLabel);
2270         quickButton->setDefinition(buttonText);
2271
2272         // Add tool tips
2273         QString toolTip=buttonText.replace('&',"&amp;").
2274             replace('<',"&lt;").
2275             replace('>',"&gt;");
2276
2277         quickButton->setToolTip(toolTip);
2278
2279         quickButton->show();
2280     } // for
2281
2282     // set hide() or show() on grid
2283     showQuickButtons(Preferences::self()->showQuickButtons());
2284 }
2285
2286 void Channel::showQuickButtons(bool show)
2287 {
2288     // Qt does not redraw the buttons properly when they are not on screen
2289     // while getting hidden, so we remember the "soon to be" state here.
2290     if(isHidden() || !m_buttonsGrid)
2291     {
2292         quickButtonsChanged=true;
2293         quickButtonsState=show;
2294     }
2295     else
2296     {
2297         if(show)
2298             m_buttonsGrid->show();
2299         else
2300             m_buttonsGrid->hide();
2301     }
2302 }
2303
2304 void Channel::showModeButtons(bool show)
2305 {
2306     // Qt does not redraw the buttons properly when they are not on screen
2307     // while getting hidden, so we remember the "soon to be" state here.
2308     if(isHidden())
2309     {
2310         modeButtonsChanged=true;
2311         modeButtonsState=show;
2312     }
2313     else
2314     {
2315         if(show)
2316         {
2317             topicSplitterHidden = false;
2318             modeBox->show();
2319             modeBox->parentWidget()->show();
2320         }
2321         else
2322         {
2323             modeBox->hide();
2324
2325             if(topicLine->isHidden())
2326             {
2327                 topicSplitterHidden = true;
2328                 modeBox->parentWidget()->hide();
2329             }
2330         }
2331     }
2332 }
2333
2334 void Channel::indicateAway(bool show)
2335 {
2336     // Qt does not redraw the label properly when they are not on screen
2337     // while getting hidden, so we remember the "soon to be" state here.
2338     if(isHidden())
2339     {
2340         awayChanged=true;
2341         awayState=show;
2342     }
2343     else
2344     {
2345         if(show)
2346             awayLabel->show();
2347         else
2348             awayLabel->hide();
2349     }
2350 }
2351
2352 void Channel::showEvent(QShowEvent*)
2353 {
2354     // If the show quick/mode button settings have changed, apply the changes now
2355     if(quickButtonsChanged)
2356     {
2357         quickButtonsChanged=false;
2358         showQuickButtons(quickButtonsState);
2359     }
2360
2361     if(modeButtonsChanged)
2362     {
2363         modeButtonsChanged=false;
2364         showModeButtons(modeButtonsState);
2365     }
2366
2367     if(awayChanged)
2368     {
2369         awayChanged=false;
2370         indicateAway(awayState);
2371     }
2372
2373     syncSplitters();
2374 }
2375
2376 void Channel::syncSplitters()
2377 {
2378     QList<int> vertSizes = Preferences::self()->topicSplitterSizes();
2379     QList<int> horizSizes = Preferences::self()->channelSplitterSizes();
2380
2381     if (vertSizes.isEmpty())
2382     {
2383         vertSizes << m_topicButton->height() << (height() - m_topicButton->height());
2384         Preferences::self()->setTopicSplitterSizes(vertSizes);
2385     }
2386
2387     if (horizSizes.isEmpty())
2388     {
2389         // An approximation of a common NICKLEN plus the width of the icon,
2390         // tested with 8pt and 10pt DejaVu Sans and Droid Sans.
2391         int listWidth = fontMetrics().averageCharWidth() * 17 + 20;
2392         horizSizes << (width() - listWidth) << listWidth;
2393         Preferences::self()->setChannelSplitterSizes(horizSizes);
2394     }
2395
2396     m_vertSplitter->setSizes(vertSizes);
2397     m_horizSplitter->setSizes(horizSizes);
2398
2399     splittersInitialized = true;
2400 }
2401
2402 void Channel::updateAppearance()
2403 {
2404     QColor fg,bg,abg;
2405
2406     if(Preferences::self()->inputFieldsBackgroundColor())
2407     {
2408         fg=Preferences::self()->color(Preferences::ChannelMessage);
2409         bg=Preferences::self()->color(Preferences::TextViewBackground);
2410         abg=Preferences::self()->color(Preferences::AlternateBackground);
2411     }
2412     else
2413     {
2414         fg = palette().windowText().color();
2415         bg = palette().base().color();
2416         abg = palette().alternateBase().color();
2417     }
2418
2419     QPalette newPalette;
2420     newPalette.setColor(QPalette::WindowText, fg);
2421     newPalette.setColor(QPalette::Text, fg);
2422     newPalette.setColor(QPalette::Base, bg);
2423     newPalette.setColor(QPalette::AlternateBase, abg);
2424
2425     channelInput->setPalette(newPalette);
2426     limit->setPalette(newPalette);
2427     topicLine->setPalette(QPalette());
2428
2429     if (Preferences::self()->customTextFont())
2430     {
2431         topicLine->setFont(Preferences::self()->textFont());
2432         channelInput->setFont(Preferences::self()->textFont());
2433         nicknameCombobox->setFont(Preferences::self()->textFont());
2434         limit->setFont(Preferences::self()->textFont());
2435     }
2436     else
2437     {
2438         topicLine->setFont(KGlobalSettings::generalFont());
2439         channelInput->setFont(KGlobalSettings::generalFont());
2440         nicknameCombobox->setFont(KGlobalSettings::generalFont());
2441         limit->setFont(KGlobalSettings::generalFont());
2442     }
2443
2444     nicknameListView->resort();
2445     nicknameListView->setPalette(newPalette);
2446
2447     if (Preferences::self()->customListFont())
2448         nicknameListView->setFont(Preferences::self()->listFont());
2449     else
2450         nicknameListView->setFont(KGlobalSettings::generalFont());
2451
2452     nicknameListView->refresh();
2453
2454     showModeButtons(Preferences::self()->showModeButtons());
2455     showNicknameList(Preferences::self()->showNickList());
2456     showNicknameBox(Preferences::self()->showNicknameBox());
2457     showTopic(Preferences::self()->showTopic());
2458     setAutoUserhost(Preferences::self()->autoUserhost());
2459
2460     updateQuickButtons(Preferences::quickButtonList());
2461
2462     // Nick sorting settings might have changed. Trigger timer
2463     if (m_delayedSortTimer)
2464     {
2465         m_delayedSortTrigger = DELAYED_SORT_TRIGGER + 1;
2466         m_delayedSortTimer->start(500 + qrand()/2000);
2467     }
2468
2469     ChatWindow::updateAppearance();
2470 }
2471
2472 void Channel::nicknameComboboxChanged()
2473 {
2474     QString newNick=nicknameCombobox->currentText();
2475     oldNick=m_server->getNickname();
2476     if (oldNick != newNick)
2477     {
2478         nicknameCombobox->setCurrentIndex(nicknameCombobox->findText(oldNick));
2479         changeNickname(newNick);
2480         // return focus to input line
2481         channelInput->setFocus();
2482     }
2483 }
2484
2485 void Channel::changeNickname(const QString& newNickname)
2486 {
2487     if (!newNickname.isEmpty())
2488         m_server->queue("NICK "+newNickname);
2489 }
2490
2491 void Channel::resetNickList()
2492 {
2493     nicknameListView->setUpdatesEnabled(false);
2494     purgeNicks();
2495 }
2496
2497 void Channel::addPendingNickList(const QStringList& pendingChannelNickList)
2498 {
2499     if(pendingChannelNickList.isEmpty())
2500       return;
2501
2502     if (!m_processingTimer)
2503     {
2504         m_processingTimer = new QTimer(this);
2505         connect(m_processingTimer, SIGNAL(timeout()), this, SLOT(processPendingNicks()));
2506     }
2507
2508     m_pendingChannelNickLists << pendingChannelNickList;
2509
2510     if (!m_processingTimer->isActive())
2511         m_processingTimer->start(0);
2512 }
2513
2514 void Channel::childAdjustFocus()
2515 {
2516     channelInput->setFocus();
2517     refreshModeButtons();
2518 }
2519
2520 void Channel::refreshModeButtons()
2521 {
2522     bool enable = true;
2523     if(getOwnChannelNick())
2524     {
2525         enable=getOwnChannelNick()->isAnyTypeOfOp();
2526     } // if not channel nick, then enable is true - fall back to assuming they are op
2527
2528     //don't disable the mode buttons since you can't then tell if they are enabled or not.
2529     //needs to be fixed somehow
2530
2531     /*  modeT->setEnabled(enable);
2532       modeN->setEnabled(enable);
2533       modeS->setEnabled(enable);
2534       modeI->setEnabled(enable);
2535       modeP->setEnabled(enable);
2536       modeM->setEnabled(enable);
2537       modeK->setEnabled(enable);
2538       modeL->setEnabled(enable);*/
2539     limit->setEnabled(enable);
2540
2541     // Tooltips for the ModeButtons
2542     QString opOnly;
2543     if(!enable) opOnly = i18n("You have to be an operator to change this.");
2544
2545     modeT->setToolTip(i18n("Topic can be changed by channel operator only.  %1", opOnly));
2546     modeN->setToolTip(i18n("No messages to channel from clients on the outside.  %1", opOnly));
2547     modeS->setToolTip(i18n("Secret channel.  %1", opOnly));
2548     modeI->setToolTip(i18n("Invite only channel.  %1", opOnly));
2549     modeP->setToolTip(i18n("Private channel.  %1", opOnly));
2550     modeM->setToolTip(i18n("Moderated channel.  %1", opOnly));
2551     modeK->setToolTip(i18n("Protect channel with a password."));
2552     modeL->setToolTip(i18n("Set user limit to channel."));
2553
2554 }
2555
2556 void Channel::cycleChannel()
2557 {
2558     closeYourself();
2559     m_server->sendJoinCommand(getName(), getPassword());
2560 }
2561
2562 void Channel::nicknameListViewTextChanged(int textChangedFlags)
2563 {
2564     m_nicknameListViewTextChanged |= textChangedFlags;
2565 }
2566
2567 void Channel::autoUserhost()
2568 {
2569     if(Preferences::self()->autoUserhost() && !Preferences::self()->autoWhoContinuousEnabled())
2570     {
2571         int limit = 5;
2572
2573         QString nickString;
2574
2575         foreach (Nick* nick, getNickList())
2576         {
2577             if(nick->getChannelNick()->getHostmask().isEmpty())
2578             {
2579                 if(limit--) nickString = nickString + nick->getChannelNick()->getNickname() + ' ';
2580                 else break;
2581             }
2582         }
2583
2584         if(!nickString.isEmpty()) m_server->requestUserhost(nickString);
2585     }
2586
2587     // Resize columns if needed (on regular basis)
2588     if (m_nicknameListViewTextChanged & (1 << Nick::NicknameColumn))
2589         nicknameListView->resizeColumnToContents(Nick::NicknameColumn);
2590     if (m_nicknameListViewTextChanged & (1 << Nick::HostmaskColumn))
2591         nicknameListView->resizeColumnToContents(Nick::HostmaskColumn);
2592     m_nicknameListViewTextChanged = 0;
2593 }
2594
2595 void Channel::setAutoUserhost(bool state)
2596 {
2597     nicknameListView->setColumnHidden(Nick::HostmaskColumn, !state);
2598     if (state)
2599     {
2600         nicknameListView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
2601         // Cannot use QHeaderView::ResizeToContents here because it is slow
2602         // and it gets triggered by setSortingEnabled(). Using timed resize
2603         // instead, see Channel::autoUserhost() above.
2604         nicknameListView->header()->setResizeMode(Nick::NicknameColumn, QHeaderView::Fixed);
2605         nicknameListView->header()->setResizeMode(Nick::HostmaskColumn, QHeaderView::Fixed);
2606         userhostTimer.start(10000);
2607         m_nicknameListViewTextChanged |= 0xFF; // ResizeColumnsToContents
2608         QTimer::singleShot(0, this, SLOT(autoUserhost())); // resize columns ASAP
2609     }
2610     else
2611     {
2612         nicknameListView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
2613         nicknameListView->header()->setResizeMode(Nick::NicknameColumn, QHeaderView::Stretch);
2614         userhostTimer.stop();
2615     }
2616 }
2617
2618 void Channel::scheduleAutoWho() // slot
2619 {
2620     if(m_whoTimer.isActive())
2621         m_whoTimer.stop();
2622     if(Preferences::self()->autoWhoContinuousEnabled())
2623         m_whoTimer.start(Preferences::self()->autoWhoContinuousInterval() * 1000);
2624 }
2625
2626 void Channel::autoWho()
2627 {
2628     // don't use auto /WHO when the number of nicks is too large, or get banned.
2629     if((nicks > Preferences::self()->autoWhoNicksLimit()) ||
2630        m_server->getInputFilter()->isWhoRequestUnderProcess(getName()))
2631     {
2632         scheduleAutoWho();
2633         return;
2634     }
2635
2636     m_server->requestWho(getName());
2637 }
2638
2639 void Channel::fadeActivity()
2640 {
2641     foreach (Nick *nick,  nicknameList) {
2642         nick->getChannelNick()->lessActive();
2643     }
2644 }
2645
2646 QString Channel::getTextInLine()
2647 {
2648   return channelInput->toPlainText();
2649 }
2650
2651 bool Channel::canBeFrontView()
2652 {
2653   return true;
2654 }
2655
2656 bool Channel::searchView()
2657 {
2658   return true;
2659 }
2660
2661 void Channel::appendInputText(const QString& s, bool fromCursor)
2662 {
2663     if(!fromCursor)
2664     {
2665         channelInput->append(s);
2666     }
2667     else
2668     {
2669         const int position = channelInput->textCursor().position();
2670         channelInput->textCursor().insertText(s);
2671         QTextCursor cursor = channelInput->textCursor();
2672         cursor.setPosition(position + s.length());
2673         channelInput->setTextCursor(cursor);
2674     }
2675 }
2676
2677 bool Channel::closeYourself(bool confirm)
2678 {
2679     int result=KMessageBox::Continue;
2680     if (confirm)
2681         result = KMessageBox::warningContinueCancel(this,
2682             i18n("Do you want to leave %1?", getName()),
2683             i18n("Leave Channel"),
2684             KGuiItem(i18n("Leave")),
2685             KStandardGuiItem::cancel(),
2686             "QuitChannelTab");
2687
2688     if (result==KMessageBox::Continue)
2689     {
2690         m_server->closeChannel(getName());
2691         m_server->removeChannel(this);
2692         deleteLater();
2693         return true;
2694     }
2695     return false;
2696 }
2697
2698 void Channel::serverOnline(bool online)
2699 {
2700     setActive(online);
2701 }
2702
2703 //Used to disable functions when not connected, does not necessarily mean the server is offline
2704 void Channel::setActive(bool active)
2705 {
2706     if (active)
2707     {
2708         getTextView()->setNickAndChannelContextMenusEnabled(true);
2709         nicknameCombobox->setEnabled(true);
2710     }
2711     else
2712     {
2713         purgeNicks();
2714         getTextView()->setNickAndChannelContextMenusEnabled(false);
2715         nicknameCombobox->setEnabled(false);
2716         topicLine->clear();
2717         clearModeList();
2718         clearBanList();
2719     }
2720 }
2721
2722 void Channel::showTopic(bool show)
2723 {
2724     if(show)
2725     {
2726         topicSplitterHidden = false;
2727         topicLine->show();
2728         m_topicButton->show();
2729         topicLine->parentWidget()->show();
2730     }
2731     else
2732     {
2733         topicLine->hide();
2734         m_topicButton->hide();
2735
2736         if(modeBox->isHidden())
2737         {
2738             topicSplitterHidden = true;
2739             topicLine->parentWidget()->hide();
2740         }
2741     }
2742 }
2743
2744 void Channel::processPendingNicks()
2745 {
2746     QString nickname = m_pendingChannelNickLists.first()[m_currentIndex];
2747
2748     bool admin = false;
2749     bool owner = false;
2750     bool op = false;
2751     bool halfop = false;
2752     bool voice = false;
2753
2754     // Remove possible mode characters from nickname and store the resulting mode
2755     m_server->mangleNicknameWithModes(nickname, admin, owner, op, halfop, voice);
2756
2757     // TODO: make these an enumeration in KApplication or somewhere, we can use them as well
2758     unsigned int mode = (admin  ? 16 : 0) +
2759                         (owner  ?  8 : 0) +
2760                         (op     ?  4 : 0) +
2761                         (halfop ?  2 : 0) +
2762                         (voice  ?  1 : 0);
2763
2764     // Check if nick is already in the nicklist
2765     if (!getNickByName(nickname))
2766     {
2767         ChannelNickPtr nick = m_server->addNickToJoinedChannelsList(getName(), nickname);
2768         Q_ASSERT(nick);
2769         nick->setMode(mode);
2770
2771         fastAddNickname(nick);
2772
2773         if (nick->isAdmin() || nick->isOwner() || nick->isOp() || nick->isHalfOp())
2774             m_opsToAdd++;
2775
2776         m_currentIndex++;
2777     }
2778     else
2779     {
2780         m_pendingChannelNickLists.first().pop_front();
2781     }
2782
2783     if (m_pendingChannelNickLists.first().count() <= m_currentIndex)
2784     {
2785         adjustNicks(m_pendingChannelNickLists.first().count());
2786         adjustOps(m_opsToAdd);
2787         m_pendingChannelNickLists.pop_front();
2788         m_currentIndex = 0;
2789         m_opsToAdd = 0;
2790     }
2791
2792     if (m_pendingChannelNickLists.isEmpty())
2793     {
2794         m_processingTimer->stop();
2795         sortNickList();
2796         nicknameListView->setUpdatesEnabled(true);
2797     }
2798 }
2799
2800 void Channel::setChannelEncoding(const QString& encoding) // virtual
2801 {
2802     if(m_server->getServerGroup())
2803         Preferences::setChannelEncoding(m_server->getServerGroup()->id(), getName(), encoding);
2804     else
2805         Preferences::setChannelEncoding(m_server->getDisplayName(), getName(), encoding);
2806 }
2807
2808 QString Channel::getChannelEncoding() // virtual
2809 {
2810     if(m_server->getServerGroup())
2811         return Preferences::channelEncoding(m_server->getServerGroup()->id(), getName());
2812     return Preferences::channelEncoding(m_server->getDisplayName(), getName());
2813 }
2814
2815 QString Channel::getChannelEncodingDefaultDesc()  // virtual
2816 {
2817     return i18n("Identity Default ( %1 )", getServer()->getIdentity()->getCodecName());
2818 }
2819
2820 void Channel::showNicknameBox(bool show)
2821 {
2822     if(show)
2823     {
2824         nicknameCombobox->show();
2825     }
2826     else
2827     {
2828         nicknameCombobox->hide();
2829     }
2830 }
2831
2832 void Channel::showNicknameList(bool show)
2833 {
2834     if (show)
2835     {
2836         channelSplitterHidden = false;
2837         nickListButtons->show();
2838     }
2839     else
2840     {
2841         channelSplitterHidden = true;
2842         nickListButtons->hide();
2843     }
2844 }
2845
2846 void Channel::requestNickListSort()
2847 {
2848     m_delayedSortTrigger++;
2849     if (m_delayedSortTrigger == DELAYED_SORT_TRIGGER &&
2850         !m_delayedSortTimer->isActive())
2851     {
2852         nicknameListView->fastSetSortingEnabled(false);
2853         m_delayedSortTimer->start(1000);
2854     }
2855 }
2856
2857 void Channel::delayedSortNickList()
2858 {
2859     sortNickList(true);
2860 }
2861
2862 void Channel::sortNickList(bool delayed)
2863 {
2864     if (!delayed || m_delayedSortTrigger > DELAYED_SORT_TRIGGER) {
2865         qSort(nicknameList.begin(), nicknameList.end(), nickLessThan);
2866         nicknameListView->resort();
2867     }
2868     if (!nicknameListView->isSortingEnabled())
2869         nicknameListView->fastSetSortingEnabled(true);
2870     m_delayedSortTrigger = 0;
2871     m_delayedSortTimer->stop();
2872 }
2873
2874 void Channel::repositionNick(Nick *nick)
2875 {
2876     int index = nicknameList.indexOf(nick);
2877
2878     if (index > -1) {
2879         // Trigger nick reposition in the nicklist including
2880         // field updates
2881         nick->refresh();
2882         // Readd nick to the nicknameList
2883         nicknameList.removeAt(index);
2884         fastAddNickname(nick->getChannelNick(), nick);
2885     } else {
2886         kWarning() << "Nickname " << nick->getChannelNick()->getNickname() << " not found!"<< endl;
2887     }
2888 }
2889
2890 bool Channel::eventFilter(QObject* watched, QEvent* e)
2891 {
2892     if((watched == nicknameListView) && (e->type() == QEvent::Resize) && splittersInitialized && isVisible())
2893     {
2894         if (!topicSplitterHidden && !channelSplitterHidden)
2895         {
2896             Preferences::self()->setChannelSplitterSizes(m_horizSplitter->sizes());
2897             Preferences::self()->setTopicSplitterSizes(m_vertSplitter->sizes());
2898         }
2899         if (!topicSplitterHidden && channelSplitterHidden)
2900         {
2901             Preferences::self()->setTopicSplitterSizes(m_vertSplitter->sizes());
2902         }
2903         if (!channelSplitterHidden && topicSplitterHidden)
2904         {
2905             Preferences::self()->setChannelSplitterSizes(m_horizSplitter->sizes());
2906         }
2907     }
2908
2909     return ChatWindow::eventFilter(watched, e);
2910 }
2911
2912 void Channel::addBan(const QString& ban)
2913 {
2914     for ( QStringList::iterator it = m_BanList.begin(); it != m_BanList.end(); ++it )
2915     {
2916         if ((*it).section(' ', 0, 0) == ban.section(' ', 0, 0))
2917         {
2918             // Ban is already in list.
2919             it = m_BanList.erase(it);
2920
2921             emit banRemoved(ban.section(' ', 0, 0));
2922             if (it == m_BanList.end())
2923                 break;
2924         }
2925     }
2926
2927     m_BanList.prepend(ban);
2928
2929     emit banAdded(ban);
2930 }
2931
2932 void Channel::removeBan(const QString& ban)
2933 {
2934   foreach(const QString &string, m_BanList)
2935   {
2936     if (string.section(' ', 0, 0) == ban)
2937     {
2938       m_BanList.removeOne(string);
2939
2940       emit banRemoved(ban);
2941     }
2942   }
2943 }
2944
2945 void Channel::clearBanList()
2946 {
2947   m_BanList.clear();
2948
2949   emit banListCleared();
2950 }
2951
2952 void Channel::append(const QString& nickname, const QString& message)
2953 {
2954     if(nickname != getServer()->getNickname()) {
2955         Nick* nick = getNickByName(nickname);
2956
2957         if(nick) {
2958             nick->getChannelNick()->setTimeStamp(QDateTime::currentDateTime().toTime_t());
2959         }
2960     }
2961
2962     ChatWindow::append(nickname, message);
2963     nickActive(nickname);
2964 }
2965
2966 void Channel::appendAction(const QString& nickname, const QString& message)
2967 {
2968     if(nickname != getServer()->getNickname()) {
2969         Nick* nick = getNickByName(nickname);
2970
2971         if(nick) {
2972             nick->getChannelNick()->setTimeStamp(QDateTime::currentDateTime().toTime_t());
2973         }
2974     }
2975
2976     ChatWindow::appendAction(nickname, message);
2977     nickActive(nickname);
2978 }
2979
2980 void Channel::nickActive(const QString& nickname) //FIXME reported to crash, can't reproduce
2981 {
2982     ChannelNickPtr channelnick=getChannelNick(nickname);
2983     //XXX Would be nice to know why it can be null here...
2984     if (channelnick) {
2985         channelnick->moreActive();
2986         Nick* nick = getNickByName(nickname); // FIXME: begs for map lookup
2987         if (nick) {
2988             nick->repositionMe();
2989         }
2990     }
2991 }
2992
2993 #ifdef HAVE_QCA2
2994 Konversation::Cipher* Channel::getCipher()
2995 {
2996     if(!m_cipher)
2997         m_cipher = new Konversation::Cipher();
2998     return m_cipher;
2999 }
3000 #endif
3001
3002 void Channel::updateNickInfos()
3003 {
3004     foreach(Nick* nick, nicknameList)
3005     {
3006         if(nick->getChannelNick()->getNickInfo()->isChanged())
3007         {
3008             nick->refresh();
3009         }
3010     }
3011 }
3012
3013 void Channel::updateChannelNicks(const QString& channel)
3014 {
3015     if(channel != name.toLower())
3016         return;
3017
3018     foreach(Nick* nick, nicknameList)
3019     {
3020         if(nick->getChannelNick()->isChanged())
3021         {
3022             nick->refresh();
3023
3024             if(nick->getChannelNick() == m_ownChannelNick)
3025             {
3026                 refreshModeButtons();
3027             }
3028         }
3029     }
3030 }
3031
3032 //
3033 // NickList
3034 //
3035
3036 NickList::NickList() : QList<Nick*>()
3037 {
3038 }
3039
3040 QString NickList::completeNick(const QString& pattern, bool& complete, QStringList& found,
3041                                bool skipNonAlfaNum, bool caseSensitive)
3042 {
3043     found.clear();
3044     QString prefix('^');
3045     QString newNick;
3046     QString prefixCharacter = Preferences::self()->prefixCharacter();
3047     NickList foundNicks;
3048
3049     if((pattern.contains(QRegExp("^(\\d|\\w)"))) && skipNonAlfaNum)
3050     {
3051         prefix = "^([^\\d\\w]|[\\_]){0,}";
3052     }
3053
3054     QRegExp regexp(prefix + QRegExp::escape(pattern));
3055     regexp.setCaseSensitivity(caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
3056
3057     foreach (Nick* nick, *this)
3058     {
3059         newNick = nick->getChannelNick()->getNickname();
3060
3061         if(!prefix.isEmpty() && newNick.contains(prefixCharacter))
3062         {
3063             newNick = newNick.section( prefixCharacter,1 );
3064         }
3065
3066         if(newNick.contains(regexp))
3067         {
3068             foundNicks.append(nick);
3069         }
3070     }
3071
3072     qSort(foundNicks.begin(), foundNicks.end(), nickTimestampLessThan);
3073
3074     foreach (Nick *nick, foundNicks)
3075     {
3076         found.append(nick->getChannelNick()->getNickname());
3077     }
3078
3079     if(found.count() > 1)
3080     {
3081         bool ok = true;
3082         int patternLength = pattern.length();
3083         QString firstNick = found[0];
3084         int firstNickLength = firstNick.length();
3085         int foundCount = found.count();
3086
3087         while(ok && ((patternLength) < firstNickLength))
3088         {
3089             ++patternLength;
3090             QStringList tmp = found.filter(firstNick.left(patternLength), caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
3091
3092             if(tmp.count() != foundCount)
3093             {
3094                 ok = false;
3095                 --patternLength;
3096             }
3097         }
3098
3099         complete = false;
3100         return firstNick.left(patternLength);
3101     }
3102     else if(found.count() == 1)
3103     {
3104         complete = true;
3105         return found[0];
3106     }
3107
3108     return QString();
3109 }
3110
3111 bool NickList::containsNick(const QString& nickname)
3112 {
3113     foreach (Nick* nick, *this)
3114     {
3115         if (nick->getChannelNick()->getNickname()==nickname)
3116             return true;
3117     }
3118
3119     return false;
3120 }
3121
3122 #include "channel.moc"
3123
3124 // kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on;
3125 // vim: set et sw=4 ts=4 cino=l1,cs,U1: