1 /*
2     SPDX-License-Identifier: GPL-2.0-or-later
3 
4     SPDX-FileCopyrightText: 2005-2007 Peter Simonsson <psn@linux.se>
5     SPDX-FileCopyrightText: 2006 Dario Abatianni <eisfuchs@tigress.com>
6     SPDX-FileCopyrightText: 2006-2007 Eike Hein <hein@kde.org>
7 */
8 
9 #include "channeloptionsdialog.h"
10 
11 #include "application.h"
12 #include "topichistorymodel.h"
13 #include "konversation_log.h"
14 
15 #include <KColorScheme>
16 #include <KSharedConfig>
17 #include <KConfigGroup>
18 
19 #include <QCheckBox>
20 #include <QPushButton>
21 #include <QRegularExpression>
22 #include <QStandardItemModel>
23 #include <QItemSelectionModel>
24 #include <QTreeWidget>
25 #include <QDialogButtonBox>
26 #include <QVBoxLayout>
27 #include <QLocale>
28 
29 namespace Konversation
30 {
ChannelOptionsDialog(Channel * channel)31     ChannelOptionsDialog::ChannelOptionsDialog(Channel *channel)
32         : QDialog(channel)
33     {
34         setWindowTitle(  i18n("Channel Settings for %1", channel->getName() ) );
35         auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
36         auto *mainWidget = new QWidget(this);
37         auto *mainLayout = new QVBoxLayout;
38         setLayout(mainLayout);
39         mainLayout->addWidget(mainWidget);
40         QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
41         okButton->setDefault(true);
42         okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
43         connect(buttonBox, &QDialogButtonBox::accepted, this, &ChannelOptionsDialog::changeOptions);
44         connect(buttonBox, &QDialogButtonBox::rejected, this, &ChannelOptionsDialog::reject);
45         mainLayout->addWidget(buttonBox);
46         buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
47 
48         Q_ASSERT(channel);
49         m_channel = channel;
50 
51         m_ui.setupUi(mainWidget);
52 
53         m_ui.addBan->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
54         m_ui.updateBan->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename")));
55         m_ui.removeBan->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
56 
57         auto *modesModel = new QStandardItemModel(m_ui.otherModesList);
58         m_ui.otherModesList->setModel(modesModel);
59         m_ui.otherModesList->hide();
60 
61         m_ui.banListSearchLine->setTreeWidget(m_ui.banList);
62 
63         m_ui.topicHistoryView->setServer(m_channel->getServer());
64         m_ui.topicHistoryView->setModel(m_channel->getTopicHistory());
65 
66         m_historySearchTimer = new QTimer(this);
67         m_historySearchTimer->setSingleShot(true);
68 
69         connect(m_historySearchTimer, &QTimer::timeout,
70                 this, &ChannelOptionsDialog::updateHistoryFilter);
71 
72         connect(m_ui.topicHistorySearchLine, &QLineEdit::textChanged,
73                 this, &ChannelOptionsDialog::startHistorySearchTimer);
74 
75         m_editingTopic = false;
76 
77         m_ui.topicEdit->setChannel(channel);
78         m_ui.topicEdit->setMaximumLength(m_channel->getServer()->topicLength());
79 
80         connect(m_ui.topicHistoryView->selectionModel(), &QItemSelectionModel::selectionChanged,
81                 this, &ChannelOptionsDialog::topicHistoryItemClicked);
82         connect(m_ui.toggleAdvancedModes, &QPushButton::clicked, this, &ChannelOptionsDialog::toggleAdvancedModes);
83         connect(m_ui.topicEdit, &TopicEdit::undoAvailable, this, &ChannelOptionsDialog::topicBeingEdited);
84         connect(this, &ChannelOptionsDialog::finished, m_ui.topicEdit, &TopicEdit::clear);
85 
86         connect(m_channel, &Channel::modesChanged, this, &ChannelOptionsDialog::refreshModes);
87         connect(m_channel->getServer(), &Server::channelNickChanged, this, [this]() { refreshEnableModes(); });
88 
89         connect(m_channel, &Channel::banAdded, this, &ChannelOptionsDialog::addBan);
90         connect(m_channel, &Channel::banRemoved, this, &ChannelOptionsDialog::removeBan);
91         connect(m_channel, &Channel::banListCleared, m_ui.banList, &QTreeWidget::clear);
92 
93         connect(m_ui.addBan, &QPushButton::clicked, this, &ChannelOptionsDialog::addBanClicked);
94         connect(m_ui.updateBan, &QPushButton::clicked, this, &ChannelOptionsDialog::updateBanClicked);
95         connect(m_ui.removeBan, &QPushButton::clicked, this, &ChannelOptionsDialog::removeBanClicked);
96         connect(m_ui.banList, &QTreeWidget::itemSelectionChanged, this, &ChannelOptionsDialog::banSelectionChanged);
97         connect(m_ui.hostmask, &KLineEdit::textChanged, this, &ChannelOptionsDialog::hostmaskChanged);
98 
99         m_ui.topicModeChBox->setWhatsThis(whatsThisForMode('T'));
100         m_ui.messageModeChBox->setWhatsThis(whatsThisForMode('N'));
101         m_ui.secretModeChBox->setWhatsThis(whatsThisForMode('S'));
102         m_ui.inviteModeChBox->setWhatsThis(whatsThisForMode('I'));
103         m_ui.moderatedModeChBox->setWhatsThis(whatsThisForMode('M'));
104         m_ui.keyModeChBox->setWhatsThis(whatsThisForMode('P'));
105         m_ui.keyModeEdit->setWhatsThis(whatsThisForMode('P'));
106         m_ui.userLimitEdit->setWhatsThis(whatsThisForMode('L'));
107         m_ui.userLimitChBox->setWhatsThis(whatsThisForMode('L'));
108 
109         refreshBanList();
110 
111         resize(QSize(450, 420));
112     }
113 
~ChannelOptionsDialog()114     ChannelOptionsDialog::~ChannelOptionsDialog()
115     {
116     }
117 
showEvent(QShowEvent * event)118     void ChannelOptionsDialog::showEvent(QShowEvent* event)
119     {
120         if (!event->spontaneous())
121         {
122             refreshAllowedChannelModes();
123             refreshModes();
124 
125             m_ui.topicEdit->clear();
126             m_editingTopic = false;
127 
128             m_ui.topicHistoryView->selectionModel()->clearSelection();
129             const QModelIndex& currentTopic = m_ui.topicHistoryView->model()->index(m_ui.topicHistoryView->model()->rowCount() - 1, 0);
130             m_ui.topicHistoryView->selectionModel()->select(currentTopic, QItemSelectionModel::Select);
131             m_ui.topicHistoryView->scrollTo(currentTopic, QAbstractItemView::EnsureVisible);
132 
133             if (!m_ui.topicEdit->isReadOnly())
134                 m_ui.topicEdit->setFocus();
135 
136             KConfigGroup config(KSharedConfig::openConfig(), "ChannelOptionsDialog");
137 
138             resize(config.readEntry("Size", sizeHint()));
139 
140             const QList<int>& sizes = config.readEntry("SplitterSizes", QList<int>());
141 
142             if (!sizes.isEmpty())
143                 m_ui.splitter->setSizes(sizes);
144 
145             Preferences::restoreColumnState(m_ui.banList, QStringLiteral("BanList ViewSettings"));
146         }
147 
148         QDialog::showEvent(event);
149     }
150 
hideEvent(QHideEvent * event)151     void ChannelOptionsDialog::hideEvent(QHideEvent* event)
152     {
153         KConfigGroup config(KSharedConfig::openConfig(), "ChannelOptionsDialog");
154 
155         config.writeEntry("Size", size());
156         config.writeEntry("SplitterSizes", m_ui.splitter->sizes());
157 
158         Preferences::saveColumnState(m_ui.banList, QStringLiteral("BanList ViewSettings"));
159 
160         QDialog::hideEvent(event);
161     }
162 
changeOptions()163     void ChannelOptionsDialog::changeOptions()
164     {
165         const QString& newTopic = topic();
166         const QString& oldTopic = m_channel->getTopic();
167 
168         if (newTopic != oldTopic)
169         {
170             // Pass a ^A so we can determine if we want to clear the channel topic.
171             if (newTopic.isEmpty())
172             {
173                 if (!oldTopic.isEmpty())
174                     m_channel->sendText(Preferences::self()->commandChar() + QLatin1String("TOPIC ") + m_channel->getName() + QLatin1String(" \x01"));
175             }
176             else
177                 m_channel->sendText(Preferences::self()->commandChar() + QLatin1String("TOPIC ") + m_channel->getName() + QLatin1Char(' ') + newTopic);
178         }
179 
180         const QStringList newModeList = modes();
181         const QStringList currentModeList = m_channel->getModeList();
182 
183         QString command(QStringLiteral("MODE %1 %2%3 %4"));
184 
185         for (const QString &mode : newModeList)
186         {
187             const QString modeString = mode.mid(1);
188             const bool plus = mode.at(0) == QLatin1Char('+');
189             const QStringList tmp = currentModeList.filter(QRegularExpression(QLatin1Char('^') + modeString));
190 
191             if(tmp.isEmpty() && plus)
192             {
193                 m_channel->getServer()->queue(command.arg(m_channel->getName(), QStringLiteral("+"),
194                                                           modeString.at(0), modeString.mid(1)));
195             }
196             else if(!tmp.isEmpty() && !plus)
197             {
198                 //FIXME: Bahamuth requires the key parameter for -k, but ircd breaks on -l with limit number.
199                 //Hence two versions of this.
200                 if (modeString.at(0) == QLatin1Char('k')) {
201                     m_channel->getServer()->queue(command.arg(m_channel->getName(), QStringLiteral("-"),
202                                                               modeString.at(0), modeString.mid(1)));
203                 } else {
204                     m_channel->getServer()->queue(command.arg(m_channel->getName(), QStringLiteral("-"),
205                                                               modeString.at(0), QString()));
206                 }
207             }
208         }
209         hide();
210     }
211 
toggleAdvancedModes()212     void ChannelOptionsDialog::toggleAdvancedModes()
213     {
214         bool ison = m_ui.toggleAdvancedModes->isChecked();
215         m_ui.otherModesList->setVisible(ison);
216         if(ison)
217         {
218             m_ui.toggleAdvancedModes->setText(i18n("&Hide Advanced Modes <<"));
219         }
220         else
221         {
222             m_ui.toggleAdvancedModes->setText(i18n("&Show Advanced Modes >>"));
223         }
224     }
225 
topicBeingEdited(bool edited)226     void ChannelOptionsDialog::topicBeingEdited(bool edited)
227     {
228         m_editingTopic = edited;
229         m_ui.topicHistoryView->setTextSelectable(edited);
230     }
231 
topic() const232     QString ChannelOptionsDialog::topic() const
233     {
234         return m_ui.topicEdit->toPlainText().replace(QLatin1Char('\n'), QLatin1Char(' '));
235     }
236 
topicHistoryItemClicked(const QItemSelection & selection)237     void ChannelOptionsDialog::topicHistoryItemClicked(const QItemSelection& selection)
238     {
239         if (!m_editingTopic)
240         {
241             m_ui.topicEdit->clear();
242 
243             if (!selection.isEmpty())
244             {
245                 m_ui.topicEdit->setUndoRedoEnabled(false);
246                 m_ui.topicEdit->setPlainText(m_ui.topicHistoryView->model()->data(selection.indexes().first()).toString());
247                 m_ui.topicEdit->setUndoRedoEnabled(true);
248             }
249         }
250     }
251 
refreshEnableModes(bool forceUpdate)252     void ChannelOptionsDialog::refreshEnableModes(bool forceUpdate)
253     {
254         if(!m_channel->getOwnChannelNick() || m_channel->getOwnChannelNick()->isChanged() || forceUpdate)
255         {
256             // cache the value
257             m_isAnyTypeOfOp = m_channel->getOwnChannelNick() ? m_channel->getOwnChannelNick()->isAnyTypeOfOp() : false;
258 
259             m_ui.topicEdit->setReadOnly(!m_isAnyTypeOfOp && m_ui.topicModeChBox->isChecked());
260 
261             m_ui.topicModeChBox->setEnabled(m_isAnyTypeOfOp);
262             m_ui.messageModeChBox->setEnabled(m_isAnyTypeOfOp);
263             m_ui.userLimitChBox->setEnabled(m_isAnyTypeOfOp);
264             m_ui.userLimitEdit->setEnabled(m_isAnyTypeOfOp);
265             m_ui.inviteModeChBox->setEnabled(m_isAnyTypeOfOp);
266             m_ui.moderatedModeChBox->setEnabled(m_isAnyTypeOfOp);
267             m_ui.secretModeChBox->setEnabled(m_isAnyTypeOfOp);
268             m_ui.keyModeChBox->setEnabled(m_isAnyTypeOfOp);
269             m_ui.keyModeEdit->setEnabled(m_isAnyTypeOfOp);
270 
271             auto* model = qobject_cast<QStandardItemModel*>(m_ui.otherModesList->model());
272 
273             if (model)
274             {
275                 QList<QStandardItem*> items = model->findItems(QStringLiteral("*"), Qt::MatchWildcard, 0);
276                 items += model->findItems(QStringLiteral("*"), Qt::MatchWildcard, 1);
277 
278                 for (QStandardItem* item : qAsConst(items))
279                     item->setEnabled(m_isAnyTypeOfOp);
280             }
281 
282             m_ui.addBan->setEnabled(m_isAnyTypeOfOp);
283             m_ui.updateBan->setEnabled(m_isAnyTypeOfOp);
284             m_ui.removeBan->setEnabled(m_isAnyTypeOfOp);
285             banSelectionChanged();
286 
287             m_ui.hostmask->setEnabled(m_isAnyTypeOfOp);
288             hostmaskChanged(m_ui.hostmask->text());
289         }
290     }
291 
refreshAllowedChannelModes()292     void ChannelOptionsDialog::refreshAllowedChannelModes()
293     {
294         QString modeString = m_channel->getServer()->allowedChannelModes();
295         // These modes are handled in a special way: ntimslkbeI
296         modeString.remove(QLatin1Char('t'));
297         modeString.remove(QLatin1Char('n'));
298         modeString.remove(QLatin1Char('l'));
299         modeString.remove(QLatin1Char('i'));
300         modeString.remove(QLatin1Char('m'));
301         modeString.remove(QLatin1Char('s'));
302         modeString.remove(QLatin1Char('k'));
303         modeString.remove(QLatin1Char('b'));
304         modeString.remove(QLatin1Char('e'));
305         modeString.remove(QLatin1Char('I'));
306         modeString.remove(QLatin1Char('O'));
307         modeString.remove(QLatin1Char('o'));
308         modeString.remove(QLatin1Char('v'));
309 
310         auto* modesModel = static_cast<QStandardItemModel *>(m_ui.otherModesList->model());
311 
312         modesModel->clear();
313         modesModel->setHorizontalHeaderLabels(QStringList { i18n("Mode"), i18n("Parameter") });
314 
315         for (const QChar mode : qAsConst(modeString)) {
316             const QString modeAsString(mode);
317 
318             QStandardItem *item = nullptr;
319             if (!Preferences::self()->useLiteralModes() && getChannelModesHash().contains(mode))
320                 item = new QStandardItem(i18nc("<mode character> (<mode description>)","%1 (%2)", mode, getChannelModesHash().value(mode)));
321             else
322                 item = new QStandardItem(modeAsString);
323             item->setData(modeAsString);
324             item->setCheckable(true);
325             item->setEditable(false);
326 
327             auto* secondItem = new QStandardItem();
328             secondItem->setEditable(true);
329 
330             const QList<QStandardItem *> newRow { item, secondItem };
331             modesModel->invisibleRootItem()->appendRow(newRow);
332         }
333     }
334 
refreshModes()335     void ChannelOptionsDialog::refreshModes()
336     {
337         const QStringList modes = m_channel->getModeList();
338 
339         m_ui.topicModeChBox->setChecked(false);
340         m_ui.messageModeChBox->setChecked(false);
341         m_ui.userLimitChBox->setChecked(false);
342         m_ui.userLimitEdit->setValue(0);
343         m_ui.inviteModeChBox->setChecked(false);
344         m_ui.moderatedModeChBox->setChecked(false);
345         m_ui.secretModeChBox->setChecked(false);
346         m_ui.keyModeChBox->setChecked(false);
347         m_ui.keyModeEdit->setText(QString());
348 
349         auto* modesModel = static_cast<QStandardItemModel *>(m_ui.otherModesList->model());
350         for (int i = 0; i < modesModel->rowCount(); ++i)
351         {
352             modesModel->item(i, 0)->setCheckState(Qt::Unchecked);
353         }
354 
355         char mode;
356 
357         for (const QString &currentMode : modes) {
358             mode = currentMode[0].toLatin1();
359             switch(mode)
360             {
361                 case 't':
362                     m_ui.topicModeChBox->setChecked(true);
363                     break;
364                 case 'n':
365                     m_ui.messageModeChBox->setChecked(true);
366                     break;
367                 case 'l':
368                     m_ui.userLimitChBox->setChecked(true);
369                     m_ui.userLimitEdit->setValue(currentMode.midRef(1).toInt());
370                     break;
371                 case 'i':
372                     m_ui.inviteModeChBox->setChecked(true);
373                     break;
374                 case 'm':
375                     m_ui.moderatedModeChBox->setChecked(true);
376                     break;
377                 case 's':
378                     m_ui.secretModeChBox->setChecked(true);
379                     break;
380                 case 'k':
381                     m_ui.keyModeChBox->setChecked(true);
382                     m_ui.keyModeEdit->setText(currentMode.mid(1));
383                     break;
384                 default:
385                 {
386                     bool found = false;
387                     QString modeString;
388                     modeString = QLatin1Char(mode);
389 
390                     for (int i = 0; !found && i < modesModel->rowCount(); ++i)
391                     {
392                         QStandardItem *item = modesModel->item(i, 0);
393                         if (item->data().toString() == modeString)
394                         {
395                             found = true;
396                             item->setCheckState(Qt::Checked);
397                             modesModel->item(i, 1)->setText(currentMode.mid(1));
398                         }
399                     }
400 
401                     break;
402                 }
403             }
404         }
405 
406         refreshEnableModes(true);
407     }
408 
modes() const409     QStringList ChannelOptionsDialog::modes() const
410     {
411         QStringList modes;
412         QString mode;
413 
414         const QString plus = QStringLiteral("+");
415         const QString minus = QStringLiteral("-");
416         mode = (m_ui.topicModeChBox->isChecked() ? plus : minus);
417         mode += QLatin1Char('t');
418         modes.append(mode);
419         mode = (m_ui.messageModeChBox->isChecked() ? plus : minus);
420         mode += QLatin1Char('n');
421         modes.append(mode);
422         mode = (m_ui.userLimitChBox->isChecked() ? plus : minus);
423         mode += QLatin1Char('l') + QString::number( m_ui.userLimitEdit->value() );
424         modes.append(mode);
425         mode = (m_ui.inviteModeChBox->isChecked() ? plus : minus);
426         mode += QLatin1Char('i');
427         modes.append(mode);
428         mode = (m_ui.moderatedModeChBox->isChecked() ? plus : minus);
429         mode += QLatin1Char('m');
430         modes.append(mode);
431         mode = (m_ui.secretModeChBox->isChecked() ? plus : minus);
432         mode += QLatin1Char('s');
433         modes.append(mode);
434 
435         if (m_ui.keyModeChBox->isChecked() && !m_ui.keyModeEdit->text().isEmpty())
436         {
437             mode = plus;
438             mode += QLatin1Char('k') + m_ui.keyModeEdit->text();
439             modes.append(mode);
440         }
441         else if (!m_ui.keyModeChBox->isChecked())
442         {
443             mode = minus;
444             mode += QLatin1Char('k') + m_ui.keyModeEdit->text();
445             modes.append(mode);
446         }
447 
448         auto* modesModel = static_cast<QStandardItemModel *>(m_ui.otherModesList->model());
449         for (int i = 0; i < modesModel->rowCount(); ++i)
450         {
451             mode = (modesModel->item(i, 0)->checkState() == Qt::Checked) ? plus : minus;
452             mode += modesModel->item(i, 0)->data().toString() + modesModel->item(i, 1)->text();
453             modes.append(mode);
454         }
455 
456         return modes;
457     }
458 
459     // Ban List tab related functions
460 
refreshBanList()461     void ChannelOptionsDialog::refreshBanList()
462     {
463         QStringList banlist = m_channel->getBanList();
464         m_ui.banList->clear();
465 
466         for (QStringList::const_iterator it = --banlist.constEnd(); it != --banlist.constBegin(); --it)
467             addBan((*it));
468     }
469 
addBan(const QString & newban)470     void ChannelOptionsDialog::addBan(const QString& newban)
471     {
472         auto *item = new BanListViewItem(m_ui.banList, newban.section(QLatin1Char(' '), 0, 0), newban.section(QLatin1Char(' '), 1, 1).section(QLatin1Char('!'), 0, 0), newban.section(QLatin1Char(' '), 2 ,2).toUInt());
473         // set item as current item
474         m_ui.banList->setCurrentItem(item);
475         // update button states
476         hostmaskChanged(m_ui.hostmask->text());
477     }
478 
removeBan(const QString & ban)479     void ChannelOptionsDialog::removeBan(const QString& ban)
480     {
481         QList<QTreeWidgetItem *> items = m_ui.banList->findItems(ban, Qt::MatchCaseSensitive | Qt::MatchExactly, 0);
482         if (!items.isEmpty())
483           delete items.at(0);
484     }
485 
addBanClicked()486     void ChannelOptionsDialog::addBanClicked()
487     {
488       QString newHostmask = m_ui.hostmask->text();
489       if (!newHostmask.isEmpty())
490         m_channel->getServer()->requestBan(QStringList(newHostmask), m_channel->getName(), QString());
491     }
492 
removeBanClicked()493     void ChannelOptionsDialog::removeBanClicked()
494     {
495       QString oldHostmask = m_ui.banList->currentItem()->text(0);
496       // We delete the existing item because it's possible the server may
497       // Modify the ban causing us not to catch it. If that happens we'll be
498       // stuck with a stale item and a new item with the modified hostmask.
499       delete m_ui.banList->currentItem();
500       // request unban
501       m_channel->getServer()->requestUnban(oldHostmask, m_channel->getName());
502     }
503 
updateBanClicked()504     void ChannelOptionsDialog::updateBanClicked()
505     {
506       QString oldHostmask = m_ui.banList->currentItem()->text(0);
507       QString newHostmask = m_ui.hostmask->text();
508       if (!newHostmask.isEmpty() && newHostmask.compare(oldHostmask))
509       {
510         // We delete the existing item because it's possible the server may
511         // Modify the ban causing us not to catch it. If that happens we'll be
512         // stuck with a stale item and a new item with the modified hostmask.
513         delete m_ui.banList->currentItem();
514         // request unban for the of the old hostmask
515         m_channel->getServer()->requestUnban(oldHostmask, m_channel->getName());
516         // request ban for the of the old hostmask
517         m_channel->getServer()->requestBan(QStringList(newHostmask), m_channel->getName(), QString());
518       }
519     }
520     /// Enables/disables updateBan and removeBan buttons depending on the currentItem of the banList
banSelectionChanged()521     void ChannelOptionsDialog::banSelectionChanged()
522     {
523       if (m_ui.banList->currentItem())
524       {
525         m_ui.updateBan->setEnabled(m_isAnyTypeOfOp);
526         m_ui.removeBan->setEnabled(m_isAnyTypeOfOp);
527         // update line edit content
528         m_ui.hostmask->setText(m_ui.banList->currentItem()->text(0));
529       }
530       else
531       {
532         m_ui.updateBan->setEnabled(false);
533         m_ui.removeBan->setEnabled(false);
534       }
535     }
536     /// Enables/disables addBan and updateBan buttons depending on the value of @p text
hostmaskChanged(const QString & text)537     void ChannelOptionsDialog::hostmaskChanged(const QString& text)
538     {
539       if (!text.trimmed().isEmpty()) {
540         if (m_isAnyTypeOfOp)
541         {
542           QList<QTreeWidgetItem*> items = m_ui.banList->findItems(text, Qt::MatchExactly | Qt::MatchCaseSensitive, 0);
543           m_ui.addBan->setEnabled(items.isEmpty());
544           m_ui.updateBan->setEnabled(items.isEmpty() && m_ui.banList->currentItem());
545         }
546       }
547       else
548       {
549         m_ui.addBan->setEnabled(false);
550         m_ui.updateBan->setEnabled(false);
551       }
552     }
553 
startHistorySearchTimer(const QString & filter)554     void ChannelOptionsDialog::startHistorySearchTimer(const QString &filter)
555     {
556         Q_UNUSED(filter)
557         m_historySearchTimer->start(300);
558     }
559 
updateHistoryFilter()560     void ChannelOptionsDialog::updateHistoryFilter()
561     {
562         auto* proxy = qobject_cast<QSortFilterProxyModel*>(m_ui.topicHistoryView->model());
563 
564         if(!proxy)
565             return;
566 
567         proxy->setFilterFixedString(m_ui.topicHistorySearchLine->text());
568     }
569 
570 
571     // This is our implementation of BanListViewItem
572 
BanListViewItem(QTreeWidget * parent)573     BanListViewItem::BanListViewItem(QTreeWidget *parent)
574       : QTreeWidgetItem()
575     {
576         parent->addTopLevelItem(this);
577     }
578 
BanListViewItem(QTreeWidget * parent,const QString & label1,const QString & label2,uint timestamp)579     BanListViewItem::BanListViewItem (QTreeWidget *parent, const QString& label1, const QString& label2,
580         uint timestamp) : QTreeWidgetItem()
581     {
582         setText(0, label1);
583         setText(1, label2);
584         m_timestamp.setMSecsSinceEpoch(static_cast<qint64>(timestamp) * 1000);
585         setText(2, QLocale().toString(m_timestamp, QLocale::ShortFormat));
586         setData(2, Qt::UserRole, m_timestamp);
587         parent->addTopLevelItem(this);
588     }
589 
operator <(const QTreeWidgetItem & item) const590     bool BanListViewItem::operator<(const QTreeWidgetItem &item) const
591     {
592         if (treeWidget()->sortColumn() == 2)
593         {
594             QVariant userdata = item.data(2, Qt::UserRole);
595             if (userdata.isValid() && userdata.type() == QVariant::DateTime)
596             {
597               return m_timestamp < userdata.toDateTime();
598             }
599         }
600 
601         return text(treeWidget()->sortColumn()) < item.text(treeWidget()->sortColumn());
602     }
603 }
604 
whatsThisForMode(char mode)605 QString Konversation::ChannelOptionsDialog::whatsThisForMode(char mode)
606 {
607     switch (mode) {
608     case 'T':
609         return i18n("<qt><p>These control the <em>mode</em> of the channel.  Only an operator can change these.</p><p>The <b>T</b>opic mode means that only the channel operator can change the topic for the channel.</p></qt>");
610     case 'N':
611         return i18n("<qt><p>These control the <em>mode</em> of the channel.  Only an operator can change these.</p><p><b>N</b>o messages from outside means users who are not in the channel cannot send messages for everybody in the channel to see.  Almost all channels have this set to prevent nuisance messages.</p></qt>");
612     case 'S':
613         return i18n("<qt><p>These control the <em>mode</em> of the channel.  Only an operator can change these.</p><p>A <b>S</b>ecret channel will not show up in the channel list, nor will any user be able to see that you are in the channel with the <em>WHOIS</em> command or anything similar.  Only the people that are in the same channel will know that you are in this channel, if this mode is set.</p></qt>");
614     case 'I':
615         return i18n("<qt><p>These control the <em>mode</em> of the channel.  Only an operator can change these.</p><p>An <b>I</b>nvite only channel means that people can only join the channel if they are invited.  To invite someone, a channel operator needs to issue the command <em>/invite nick</em> from within the channel.</p></qt>");
616     case 'P':
617         return i18n("<qt><p>These control the <em>mode</em> of the channel.  Only an operator can change these.</p><p>A <b>P</b>rivate channel is shown in a listing of all channels, but the topic is not shown.  A user's <em>WHOIS</em> may or may not show them as being in a private channel depending on the IRC server.</p></qt>");
618     case 'M':
619         return i18n("<qt><p>These control the <em>mode</em> of the channel.  Only an operator can change these.</p><p>A <b>M</b>oderated channel is one where only operators, half-operators and those with voice can talk.</p></qt>");
620     case 'K':
621         return i18n("<qt><p>These control the <em>mode</em> of the channel.  Only an operator can change these.</p><p>A protected channel requires users to enter a password in order to join.</p></qt>");
622     case 'L':
623         return i18n("<qt><p>These control the <em>mode</em> of the channel.  Only an operator can change these.</p><p>A channel that has a user <b>L</b>imit means that only that many users can be in the channel at any one time.  Some channels have a bot that sits in the channel and changes this automatically depending on how busy the channel is.</p></qt>");
624     default:
625         qCWarning(KONVERSATION_LOG) << "called for unknown mode" << mode;
626         return QString();
627     }
628 }
629 
630 
631