modernize: Pass arguments by value and move in constructors
[quassel.git] / src / qtui / settingspages / networkssettingspage.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include <QHeaderView>
22 #include <QMessageBox>
23 #include <QTextCodec>
24 #include <utility>
25
26 #include "networkssettingspage.h"
27
28 #include "client.h"
29 #include "icon.h"
30 #include "identity.h"
31 #include "network.h"
32 #include "presetnetworks.h"
33 #include "settingspagedlg.h"
34 #include "util.h"
35
36 // IRCv3 capabilities
37 #include "irccap.h"
38
39 #include "settingspages/identitiessettingspage.h"
40
41 NetworksSettingsPage::NetworksSettingsPage(QWidget *parent)
42     : SettingsPage(tr("IRC"), tr("Networks"), parent)
43 #ifdef HAVE_SSL
44       , _cid(nullptr)
45 #endif
46 {
47     ui.setupUi(this);
48
49     // hide SASL options for older cores
50     if (!Client::isCoreFeatureEnabled(Quassel::Feature::SaslAuthentication))
51         ui.sasl->hide();
52     if (!Client::isCoreFeatureEnabled(Quassel::Feature::SaslExternal))
53         ui.saslExtInfo->hide();
54 #ifndef HAVE_SSL
55     ui.saslExtInfo->hide();
56 #endif
57
58     // set up icons
59     ui.renameNetwork->setIcon(icon::get("edit-rename"));
60     ui.addNetwork->setIcon(icon::get("list-add"));
61     ui.deleteNetwork->setIcon(icon::get("edit-delete"));
62     ui.addServer->setIcon(icon::get("list-add"));
63     ui.deleteServer->setIcon(icon::get("edit-delete"));
64     ui.editServer->setIcon(icon::get("configure"));
65     ui.upServer->setIcon(icon::get("go-up"));
66     ui.downServer->setIcon(icon::get("go-down"));
67     ui.editIdentities->setIcon(icon::get("configure"));
68
69     _ignoreWidgetChanges = false;
70
71     connectedIcon = icon::get("network-connect");
72     connectingIcon = icon::get("network-wired"); // FIXME network-connecting
73     disconnectedIcon = icon::get("network-disconnect");
74
75     // Status icons
76     infoIcon = icon::get("dialog-information");
77     warningIcon = icon::get("dialog-warning");
78
79     foreach(int mib, QTextCodec::availableMibs()) {
80         QByteArray codec = QTextCodec::codecForMib(mib)->name();
81         ui.sendEncoding->addItem(codec);
82         ui.recvEncoding->addItem(codec);
83         ui.serverEncoding->addItem(codec);
84     }
85     ui.sendEncoding->model()->sort(0);
86     ui.recvEncoding->model()->sort(0);
87     ui.serverEncoding->model()->sort(0);
88     currentId = 0;
89     setEnabled(Client::isConnected()); // need a core connection!
90     setWidgetStates();
91     connect(Client::instance(), SIGNAL(coreConnectionStateChanged(bool)), this, SLOT(coreConnectionStateChanged(bool)));
92     connect(Client::instance(), SIGNAL(networkCreated(NetworkId)), this, SLOT(clientNetworkAdded(NetworkId)));
93     connect(Client::instance(), SIGNAL(networkRemoved(NetworkId)), this, SLOT(clientNetworkRemoved(NetworkId)));
94     connect(Client::instance(), SIGNAL(identityCreated(IdentityId)), this, SLOT(clientIdentityAdded(IdentityId)));
95     connect(Client::instance(), SIGNAL(identityRemoved(IdentityId)), this, SLOT(clientIdentityRemoved(IdentityId)));
96
97     connect(ui.identityList, SIGNAL(currentIndexChanged(int)), this, SLOT(widgetHasChanged()));
98     //connect(ui.randomServer, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
99     connect(ui.performEdit, SIGNAL(textChanged()), this, SLOT(widgetHasChanged()));
100     connect(ui.sasl, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
101     connect(ui.saslAccount, SIGNAL(textEdited(QString)), this, SLOT(widgetHasChanged()));
102     connect(ui.saslPassword, SIGNAL(textEdited(QString)), this, SLOT(widgetHasChanged()));
103     connect(ui.autoIdentify, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
104     connect(ui.autoIdentifyService, SIGNAL(textEdited(const QString &)), this, SLOT(widgetHasChanged()));
105     connect(ui.autoIdentifyPassword, SIGNAL(textEdited(const QString &)), this, SLOT(widgetHasChanged()));
106     connect(ui.useCustomEncodings, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
107     connect(ui.sendEncoding, SIGNAL(currentIndexChanged(int)), this, SLOT(widgetHasChanged()));
108     connect(ui.recvEncoding, SIGNAL(currentIndexChanged(int)), this, SLOT(widgetHasChanged()));
109     connect(ui.serverEncoding, SIGNAL(currentIndexChanged(int)), this, SLOT(widgetHasChanged()));
110     connect(ui.autoReconnect, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
111     connect(ui.reconnectInterval, SIGNAL(valueChanged(int)), this, SLOT(widgetHasChanged()));
112     connect(ui.reconnectRetries, SIGNAL(valueChanged(int)), this, SLOT(widgetHasChanged()));
113     connect(ui.unlimitedRetries, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
114     connect(ui.rejoinOnReconnect, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
115
116     // Core features can change during a reconnect.  Always connect these here, delaying testing for
117     // the core feature flag in load().
118     connect(ui.useCustomMessageRate, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
119     connect(ui.messageRateBurstSize, SIGNAL(valueChanged(int)), this, SLOT(widgetHasChanged()));
120     connect(ui.messageRateDelay, SIGNAL(valueChanged(double)), this, SLOT(widgetHasChanged()));
121     connect(ui.unlimitedMessageRate, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
122
123     // Add additional widgets here
124     //connect(ui., SIGNAL(), this, SLOT(widgetHasChanged()));
125     //connect(ui., SIGNAL(), this, SLOT(widgetHasChanged()));
126
127     foreach(IdentityId id, Client::identityIds()) {
128         clientIdentityAdded(id);
129     }
130 }
131
132
133 void NetworksSettingsPage::save()
134 {
135     setEnabled(false);
136     if (currentId != 0) saveToNetworkInfo(networkInfos[currentId]);
137
138     QList<NetworkInfo> toCreate, toUpdate;
139     QList<NetworkId> toRemove;
140     QHash<NetworkId, NetworkInfo>::iterator i = networkInfos.begin();
141     while (i != networkInfos.end()) {
142         NetworkId id = (*i).networkId;
143         if (id < 0) {
144             toCreate.append(*i);
145             //if(id == currentId) currentId = 0;
146             //QList<QListWidgetItem *> items = ui.networkList->findItems((*i).networkName, Qt::MatchExactly);
147             //if(items.count()) {
148             //  Q_ASSERT(items[0]->data(Qt::UserRole).value<NetworkId>() == id);
149             //  delete items[0];
150             //}
151             //i = networkInfos.erase(i);
152             ++i;
153         }
154         else {
155             if ((*i) != Client::network((*i).networkId)->networkInfo()) {
156                 toUpdate.append(*i);
157             }
158             ++i;
159         }
160     }
161     foreach(NetworkId id, Client::networkIds()) {
162         if (!networkInfos.contains(id)) toRemove.append(id);
163     }
164     SaveNetworksDlg dlg(toCreate, toUpdate, toRemove, this);
165     int ret = dlg.exec();
166     if (ret == QDialog::Rejected) {
167         // canceled -> reload everything to be safe
168         load();
169     }
170     setChangedState(false);
171     setEnabled(true);
172 }
173
174
175 void NetworksSettingsPage::load()
176 {
177     reset();
178
179     // Handle UI dependent on core feature flags here
180     if (Client::isCoreFeatureEnabled(Quassel::Feature::CustomRateLimits)) {
181         // Custom rate limiting supported, allow toggling
182         ui.useCustomMessageRate->setEnabled(true);
183         // Reset tooltip to default.
184         ui.useCustomMessageRate->setToolTip(QString("%1").arg(
185                                           tr("<p>Override default message rate limiting.</p>"
186                                              "<p><b>Setting limits too low may get you disconnected"
187                                              " from the server!</b></p>")));
188         // If changed, update the message below!
189     } else {
190         // Custom rate limiting not supported, disallow toggling
191         ui.useCustomMessageRate->setEnabled(false);
192         // Split up the message to allow re-using translations:
193         // [Original tool-tip]
194         // [Bold 'does not support feature' message]
195         // [Specific version needed and feature details]
196         ui.useCustomMessageRate->setToolTip(QString("%1<br/><b>%2</b><br/>%3").arg(
197                                           tr("<p>Override default message rate limiting.</p>"
198                                              "<p><b>Setting limits too low may get you disconnected"
199                                              " from the server!</b></p>"),
200                                           tr("Your Quassel core does not support this feature"),
201                                           tr("You need a Quassel core v0.13.0 or newer in order to "
202                                           "modify message rate limits.")));
203     }
204
205 #ifdef HAVE_SSL
206     // Hide the SASL EXTERNAL notice until a network's shown.  Stops it from showing while loading
207     // backlog from the core.
208     sslUpdated();
209 #endif
210
211     // Reset network capability status in case no valid networks get selected (a rare situation)
212     resetNetworkCapStates();
213
214     foreach(NetworkId netid, Client::networkIds()) {
215         clientNetworkAdded(netid);
216     }
217     ui.networkList->setCurrentRow(0);
218
219     setChangedState(false);
220 }
221
222
223 void NetworksSettingsPage::reset()
224 {
225     currentId = 0;
226     ui.networkList->clear();
227     networkInfos.clear();
228 }
229
230
231 bool NetworksSettingsPage::aboutToSave()
232 {
233     if (currentId != 0) saveToNetworkInfo(networkInfos[currentId]);
234     QList<int> errors;
235     foreach(NetworkInfo info, networkInfos.values()) {
236         if (!info.serverList.count()) errors.append(1);
237     }
238     if (!errors.count()) return true;
239     QString error(tr("<b>The following problems need to be corrected before your changes can be applied:</b><ul>"));
240     if (errors.contains(1)) error += tr("<li>All networks need at least one server defined</li>");
241     error += tr("</ul>");
242     QMessageBox::warning(this, tr("Invalid Network Settings"), error);
243     return false;
244 }
245
246
247 void NetworksSettingsPage::widgetHasChanged()
248 {
249     if (_ignoreWidgetChanges) return;
250     bool changed = testHasChanged();
251     if (changed != hasChanged()) setChangedState(changed);
252 }
253
254
255 bool NetworksSettingsPage::testHasChanged()
256 {
257     if (currentId != 0) {
258         saveToNetworkInfo(networkInfos[currentId]);
259     }
260     if (Client::networkIds().count() != networkInfos.count()) return true;
261     foreach(NetworkId id, networkInfos.keys()) {
262         if (id < 0) return true;
263         if (Client::network(id)->networkInfo() != networkInfos[id]) return true;
264     }
265     return false;
266 }
267
268
269 void NetworksSettingsPage::setWidgetStates()
270 {
271     // network list
272     if (ui.networkList->selectedItems().count()) {
273         ui.detailsBox->setEnabled(true);
274         ui.renameNetwork->setEnabled(true);
275         ui.deleteNetwork->setEnabled(true);
276
277         /* button disabled for now
278         NetworkId id = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
279         const Network *net = id > 0 ? Client::network(id) : 0;
280         ui.connectNow->setEnabled(net);
281         //    && (Client::network(id)->connectionState() == Network::Initialized
282         //    || Client::network(id)->connectionState() == Network::Disconnected));
283         if(net) {
284           if(net->connectionState() == Network::Disconnected) {
285             ui.connectNow->setIcon(connectedIcon);
286             ui.connectNow->setText(tr("Connect"));
287           } else {
288             ui.connectNow->setIcon(disconnectedIcon);
289             ui.connectNow->setText(tr("Disconnect"));
290           }
291         } else {
292           ui.connectNow->setIcon(QIcon());
293           ui.connectNow->setText(tr("Apply first!"));
294         } */
295     }
296     else {
297         ui.renameNetwork->setEnabled(false);
298         ui.deleteNetwork->setEnabled(false);
299         //ui.connectNow->setEnabled(false);
300         ui.detailsBox->setEnabled(false);
301     }
302     // network details
303     if (ui.serverList->selectedItems().count()) {
304         ui.editServer->setEnabled(true);
305         ui.deleteServer->setEnabled(true);
306         ui.upServer->setEnabled(ui.serverList->currentRow() > 0);
307         ui.downServer->setEnabled(ui.serverList->currentRow() < ui.serverList->count() - 1);
308     }
309     else {
310         ui.editServer->setEnabled(false);
311         ui.deleteServer->setEnabled(false);
312         ui.upServer->setEnabled(false);
313         ui.downServer->setEnabled(false);
314     }
315 }
316
317
318 void NetworksSettingsPage::setItemState(NetworkId id, QListWidgetItem *item)
319 {
320     if (!item && !(item = networkItem(id))) return;
321     const Network *net = Client::network(id);
322     if (!net || net->isInitialized()) item->setFlags(item->flags() | Qt::ItemIsEnabled);
323     else item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
324     if (net && net->connectionState() == Network::Initialized) {
325         item->setIcon(connectedIcon);
326     }
327     else if (net && net->connectionState() != Network::Disconnected) {
328         item->setIcon(connectingIcon);
329     }
330     else {
331         item->setIcon(disconnectedIcon);
332     }
333     if (net) {
334         bool select = false;
335         // check if we already have another net of this name in the list, and replace it
336         QList<QListWidgetItem *> items = ui.networkList->findItems(net->networkName(), Qt::MatchExactly);
337         if (items.count()) {
338             foreach(QListWidgetItem *i, items) {
339                 NetworkId oldid = i->data(Qt::UserRole).value<NetworkId>();
340                 if (oldid > 0) continue;  // only locally created nets should be replaced
341                 if (oldid == currentId) {
342                     select = true;
343                     currentId = 0;
344                     ui.networkList->clearSelection();
345                 }
346                 int row = ui.networkList->row(i);
347                 if (row >= 0) {
348                     QListWidgetItem *olditem = ui.networkList->takeItem(row);
349                     Q_ASSERT(olditem);
350                     delete olditem;
351                 }
352                 networkInfos.remove(oldid);
353                 break;
354             }
355         }
356         item->setText(net->networkName());
357         if (select) item->setSelected(true);
358     }
359 }
360
361
362 void NetworksSettingsPage::resetNetworkCapStates()
363 {
364     // Set the status to a blank (invalid) network ID, reseting all UI
365     setNetworkCapStates(NetworkId());
366 }
367
368
369 void NetworksSettingsPage::setNetworkCapStates(NetworkId id)
370 {
371     const Network *net = Client::network(id);
372     if (net && Client::isCoreFeatureEnabled(Quassel::Feature::CapNegotiation)) {
373         // Capability negotiation is supported, network exists.
374         // Check if the network is connected.  Don't use net->isConnected() as that won't be true
375         // during capability negotiation when capabilities are added and removed.
376         if (net->connectionState() != Network::Disconnected) {
377             // Network exists and is connected, check available capabilities...
378             // [SASL]
379             if (net->saslMaybeSupports(IrcCap::SaslMech::PLAIN)) {
380                 setSASLStatus(CapSupportStatus::MaybeSupported);
381             } else {
382                 setSASLStatus(CapSupportStatus::MaybeUnsupported);
383             }
384
385             // Add additional capability-dependent interface updates here
386         } else {
387             // Network is disconnected
388             // [SASL]
389             setSASLStatus(CapSupportStatus::Disconnected);
390
391             // Add additional capability-dependent interface updates here
392         }
393     } else {
394         // Capability negotiation is not supported and/or network doesn't exist.
395         // Don't assume anything and reset all capability-dependent interface elements to neutral.
396         // [SASL]
397         setSASLStatus(CapSupportStatus::Unknown);
398
399         // Add additional capability-dependent interface updates here
400     }
401 }
402
403
404 void NetworksSettingsPage::coreConnectionStateChanged(bool state)
405 {
406     this->setEnabled(state);
407     if (state) {
408         load();
409     }
410     else {
411         // reset
412         //currentId = 0;
413     }
414 }
415
416
417 void NetworksSettingsPage::clientIdentityAdded(IdentityId id)
418 {
419     const Identity *identity = Client::identity(id);
420     connect(identity, SIGNAL(updatedRemotely()), this, SLOT(clientIdentityUpdated()));
421
422     QString name = identity->identityName();
423     for (int j = 0; j < ui.identityList->count(); j++) {
424         if ((j > 0 || ui.identityList->itemData(0).toInt() != 1) && name.localeAwareCompare(ui.identityList->itemText(j)) < 0) {
425             ui.identityList->insertItem(j, name, id.toInt());
426             widgetHasChanged();
427             return;
428         }
429     }
430     // append
431     ui.identityList->insertItem(ui.identityList->count(), name, id.toInt());
432     widgetHasChanged();
433 }
434
435
436 void NetworksSettingsPage::clientIdentityUpdated()
437 {
438     const Identity *identity = qobject_cast<const Identity *>(sender());
439     if (!identity) {
440         qWarning() << "NetworksSettingsPage: Invalid identity to update!";
441         return;
442     }
443     int row = ui.identityList->findData(identity->id().toInt());
444     if (row < 0) {
445         qWarning() << "NetworksSettingsPage: Invalid identity to update!";
446         return;
447     }
448     if (ui.identityList->itemText(row) != identity->identityName()) {
449         ui.identityList->setItemText(row, identity->identityName());
450     }
451 }
452
453
454 void NetworksSettingsPage::clientIdentityRemoved(IdentityId id)
455 {
456     IdentityId defaultId = defaultIdentity();
457     if (currentId != 0) saveToNetworkInfo(networkInfos[currentId]);
458     foreach(NetworkInfo info, networkInfos.values()) {
459         if (info.identity == id) {
460             if (info.networkId == currentId)
461                 ui.identityList->setCurrentIndex(0);
462             info.identity = defaultId;
463             networkInfos[info.networkId] = info;
464             if (info.networkId > 0) Client::updateNetwork(info);
465         }
466     }
467     ui.identityList->removeItem(ui.identityList->findData(id.toInt()));
468     widgetHasChanged();
469 }
470
471
472 QListWidgetItem *NetworksSettingsPage::networkItem(NetworkId id) const
473 {
474     for (int i = 0; i < ui.networkList->count(); i++) {
475         QListWidgetItem *item = ui.networkList->item(i);
476         if (item->data(Qt::UserRole).value<NetworkId>() == id) return item;
477     }
478     return nullptr;
479 }
480
481
482 void NetworksSettingsPage::clientNetworkAdded(NetworkId id)
483 {
484     insertNetwork(id);
485     //connect(Client::network(id), SIGNAL(updatedRemotely()), this, SLOT(clientNetworkUpdated()));
486     connect(Client::network(id), SIGNAL(configChanged()), this, SLOT(clientNetworkUpdated()));
487
488     connect(Client::network(id), SIGNAL(connectionStateSet(Network::ConnectionState)), this, SLOT(networkConnectionStateChanged(Network::ConnectionState)));
489     connect(Client::network(id), SIGNAL(connectionError(const QString &)), this, SLOT(networkConnectionError(const QString &)));
490
491     // Handle capability changes in case a server dis/connects with the settings window open.
492     connect(Client::network(id), SIGNAL(capAdded(const QString &)), this, SLOT(clientNetworkCapsUpdated()));
493     connect(Client::network(id), SIGNAL(capRemoved(const QString &)), this, SLOT(clientNetworkCapsUpdated()));
494 }
495
496
497 void NetworksSettingsPage::clientNetworkUpdated()
498 {
499     const Network *net = qobject_cast<const Network *>(sender());
500     if (!net) {
501         qWarning() << "Update request for unknown network received!";
502         return;
503     }
504     networkInfos[net->networkId()] = net->networkInfo();
505     setItemState(net->networkId());
506     if (net->networkId() == currentId) displayNetwork(net->networkId());
507     setWidgetStates();
508     widgetHasChanged();
509 }
510
511
512 void NetworksSettingsPage::clientNetworkRemoved(NetworkId id)
513 {
514     if (!networkInfos.contains(id)) return;
515     if (id == currentId) displayNetwork(0);
516     NetworkInfo info = networkInfos.take(id);
517     QList<QListWidgetItem *> items = ui.networkList->findItems(info.networkName, Qt::MatchExactly);
518     foreach(QListWidgetItem *item, items) {
519         if (item->data(Qt::UserRole).value<NetworkId>() == id)
520             delete ui.networkList->takeItem(ui.networkList->row(item));
521     }
522     setWidgetStates();
523     widgetHasChanged();
524 }
525
526
527 void NetworksSettingsPage::networkConnectionStateChanged(Network::ConnectionState state)
528 {
529     Q_UNUSED(state);
530     const Network *net = qobject_cast<const Network *>(sender());
531     if (!net) return;
532     /*
533     if(net->networkId() == currentId) {
534       ui.connectNow->setEnabled(state == Network::Initialized || state == Network::Disconnected);
535     }
536     */
537     setItemState(net->networkId());
538     if (net->networkId() == currentId) {
539         // Network is currently shown.  Update the capability-dependent UI in case capabilities have
540         // changed.
541         setNetworkCapStates(currentId);
542     }
543     setWidgetStates();
544 }
545
546
547 void NetworksSettingsPage::networkConnectionError(const QString &)
548 {
549 }
550
551
552 QListWidgetItem *NetworksSettingsPage::insertNetwork(NetworkId id)
553 {
554     NetworkInfo info = Client::network(id)->networkInfo();
555     networkInfos[id] = info;
556     return insertNetwork(info);
557 }
558
559
560 QListWidgetItem *NetworksSettingsPage::insertNetwork(const NetworkInfo &info)
561 {
562     QListWidgetItem *item = nullptr;
563     QList<QListWidgetItem *> items = ui.networkList->findItems(info.networkName, Qt::MatchExactly);
564     if (!items.count()) item = new QListWidgetItem(disconnectedIcon, info.networkName, ui.networkList);
565     else {
566         // we overwrite an existing net if it a) has the same name and b) has a negative ID meaning we created it locally before
567         // -> then we can be sure that this is the core-side replacement for the net we created
568         foreach(QListWidgetItem *i, items) {
569             NetworkId id = i->data(Qt::UserRole).value<NetworkId>();
570             if (id < 0) { item = i; break; }
571         }
572         if (!item) item = new QListWidgetItem(disconnectedIcon, info.networkName, ui.networkList);
573     }
574     item->setData(Qt::UserRole, QVariant::fromValue<NetworkId>(info.networkId));
575     setItemState(info.networkId, item);
576     widgetHasChanged();
577     return item;
578 }
579
580
581 // Called when selecting 'Configure' from the buffer list
582 void NetworksSettingsPage::bufferList_Open(NetworkId netId)
583 {
584     QListWidgetItem *item = networkItem(netId);
585     ui.networkList->setCurrentItem(item, QItemSelectionModel::SelectCurrent);
586 }
587
588
589 void NetworksSettingsPage::displayNetwork(NetworkId id)
590 {
591     _ignoreWidgetChanges = true;
592     if (id != 0) {
593         NetworkInfo info = networkInfos[id];
594
595 #ifdef HAVE_SSL
596         // this is only needed when the core supports SASL EXTERNAL
597         if (Client::isCoreFeatureEnabled(Quassel::Feature::SaslExternal)) {
598             if (_cid) {
599                 disconnect(_cid, SIGNAL(sslSettingsUpdated()), this, SLOT(sslUpdated()));
600                 delete _cid;
601             }
602             _cid = new CertIdentity(*Client::identity(info.identity), this);
603             _cid->enableEditSsl(true);
604             connect(_cid, SIGNAL(sslSettingsUpdated()), this, SLOT(sslUpdated()));
605         }
606 #endif
607
608         ui.identityList->setCurrentIndex(ui.identityList->findData(info.identity.toInt()));
609         ui.serverList->clear();
610         foreach(Network::Server server, info.serverList) {
611             QListWidgetItem *item = new QListWidgetItem(QString("%1:%2").arg(server.host).arg(server.port));
612             if (server.useSsl)
613                 item->setIcon(icon::get("document-encrypt"));
614             ui.serverList->addItem(item);
615         }
616         //setItemState(id);
617         //ui.randomServer->setChecked(info.useRandomServer);
618         // Update the capability-dependent UI in case capabilities have changed.
619         setNetworkCapStates(id);
620         ui.performEdit->setPlainText(info.perform.join("\n"));
621         ui.autoIdentify->setChecked(info.useAutoIdentify);
622         ui.autoIdentifyService->setText(info.autoIdentifyService);
623         ui.autoIdentifyPassword->setText(info.autoIdentifyPassword);
624         ui.sasl->setChecked(info.useSasl);
625         ui.saslAccount->setText(info.saslAccount);
626         ui.saslPassword->setText(info.saslPassword);
627         if (info.codecForEncoding.isEmpty()) {
628             ui.sendEncoding->setCurrentIndex(ui.sendEncoding->findText(Network::defaultCodecForEncoding()));
629             ui.recvEncoding->setCurrentIndex(ui.recvEncoding->findText(Network::defaultCodecForDecoding()));
630             ui.serverEncoding->setCurrentIndex(ui.serverEncoding->findText(Network::defaultCodecForServer()));
631             ui.useCustomEncodings->setChecked(false);
632         }
633         else {
634             ui.sendEncoding->setCurrentIndex(ui.sendEncoding->findText(info.codecForEncoding));
635             ui.recvEncoding->setCurrentIndex(ui.recvEncoding->findText(info.codecForDecoding));
636             ui.serverEncoding->setCurrentIndex(ui.serverEncoding->findText(info.codecForServer));
637             ui.useCustomEncodings->setChecked(true);
638         }
639         ui.autoReconnect->setChecked(info.useAutoReconnect);
640         ui.reconnectInterval->setValue(info.autoReconnectInterval);
641         ui.reconnectRetries->setValue(info.autoReconnectRetries);
642         ui.unlimitedRetries->setChecked(info.unlimitedReconnectRetries);
643         ui.rejoinOnReconnect->setChecked(info.rejoinChannels);
644         // Custom rate limiting
645         ui.unlimitedMessageRate->setChecked(info.unlimitedMessageRate);
646         // Set 'ui.useCustomMessageRate' after 'ui.unlimitedMessageRate' so if the latter is
647         // disabled, 'ui.messageRateDelayFrame' will remain disabled.
648         ui.useCustomMessageRate->setChecked(info.useCustomMessageRate);
649         ui.messageRateBurstSize->setValue(info.messageRateBurstSize);
650         // Convert milliseconds (integer) into seconds (double)
651         ui.messageRateDelay->setValue(info.messageRateDelay / 1000.0f);
652     }
653     else {
654         // just clear widgets
655 #ifdef HAVE_SSL
656         if (_cid) {
657             disconnect(_cid, SIGNAL(sslSettingsUpdated()), this, SLOT(sslUpdated()));
658             delete _cid;
659         }
660 #endif
661         ui.identityList->setCurrentIndex(-1);
662         ui.serverList->clear();
663         ui.performEdit->clear();
664         ui.autoIdentifyService->clear();
665         ui.autoIdentifyPassword->clear();
666         ui.saslAccount->clear();
667         ui.saslPassword->clear();
668         setWidgetStates();
669     }
670     _ignoreWidgetChanges = false;
671     currentId = id;
672 }
673
674
675 void NetworksSettingsPage::saveToNetworkInfo(NetworkInfo &info)
676 {
677     info.identity = ui.identityList->itemData(ui.identityList->currentIndex()).toInt();
678     //info.useRandomServer = ui.randomServer->isChecked();
679     info.perform = ui.performEdit->toPlainText().split("\n");
680     info.useAutoIdentify = ui.autoIdentify->isChecked();
681     info.autoIdentifyService = ui.autoIdentifyService->text();
682     info.autoIdentifyPassword = ui.autoIdentifyPassword->text();
683     info.useSasl = ui.sasl->isChecked();
684     info.saslAccount = ui.saslAccount->text();
685     info.saslPassword = ui.saslPassword->text();
686     if (!ui.useCustomEncodings->isChecked()) {
687         info.codecForEncoding.clear();
688         info.codecForDecoding.clear();
689         info.codecForServer.clear();
690     }
691     else {
692         info.codecForEncoding = ui.sendEncoding->currentText().toLatin1();
693         info.codecForDecoding = ui.recvEncoding->currentText().toLatin1();
694         info.codecForServer = ui.serverEncoding->currentText().toLatin1();
695     }
696     info.useAutoReconnect = ui.autoReconnect->isChecked();
697     info.autoReconnectInterval = ui.reconnectInterval->value();
698     info.autoReconnectRetries = ui.reconnectRetries->value();
699     info.unlimitedReconnectRetries = ui.unlimitedRetries->isChecked();
700     info.rejoinChannels = ui.rejoinOnReconnect->isChecked();
701     // Custom rate limiting
702     info.useCustomMessageRate = ui.useCustomMessageRate->isChecked();
703     info.messageRateBurstSize = ui.messageRateBurstSize->value();
704     // Convert seconds (double) into milliseconds (integer)
705     info.messageRateDelay = static_cast<quint32>((ui.messageRateDelay->value() * 1000));
706     info.unlimitedMessageRate = ui.unlimitedMessageRate->isChecked();
707 }
708
709
710 void NetworksSettingsPage::clientNetworkCapsUpdated()
711 {
712     // Grab the updated network
713     const Network *net = qobject_cast<const Network *>(sender());
714     if (!net) {
715         qWarning() << "Update request for unknown network received!";
716         return;
717     }
718     if (net->networkId() == currentId) {
719         // Network is currently shown.  Update the capability-dependent UI in case capabilities have
720         // changed.
721         setNetworkCapStates(currentId);
722     }
723 }
724
725
726 void NetworksSettingsPage::setSASLStatus(const CapSupportStatus saslStatus)
727 {
728     if (_saslStatusSelected != saslStatus) {
729         // Update the cached copy of SASL status used with the Details dialog
730         _saslStatusSelected = saslStatus;
731
732         // Update the user interface
733         switch (saslStatus) {
734             case CapSupportStatus::Unknown:
735                 // There's no capability negotiation or network doesn't exist.  Don't assume
736                 // anything.
737                 ui.saslStatusLabel->setText(QString("<i>%1</i>").arg(
738                                             tr("Could not check if supported by network")));
739                 ui.saslStatusIcon->setPixmap(infoIcon.pixmap(16));
740                 break;
741             case CapSupportStatus::Disconnected:
742                 // Disconnected from network, no way to check.
743                 ui.saslStatusLabel->setText(QString("<i>%1</i>").arg(
744                                                 tr("Cannot check if supported when disconnected")));
745                 ui.saslStatusIcon->setPixmap(infoIcon.pixmap(16));
746                 break;
747             case CapSupportStatus::MaybeUnsupported:
748                 // The network doesn't advertise support for SASL PLAIN.  Here be dragons.
749                 ui.saslStatusLabel->setText(QString("<i>%1</i>").arg(
750                                                 tr("Not currently supported by network")));
751                 ui.saslStatusIcon->setPixmap(warningIcon.pixmap(16));
752                 break;
753             case CapSupportStatus::MaybeSupported:
754                 // The network advertises support for SASL PLAIN.  Encourage using it!
755                 // Unfortunately we don't know for sure if it's desired or functional.
756                 ui.saslStatusLabel->setText(QString("<i>%1</i>").arg(tr("Supported by network")));
757                 ui.saslStatusIcon->setPixmap(infoIcon.pixmap(16));
758                 break;
759         }
760     }
761 }
762
763
764 #ifdef HAVE_SSL
765 void NetworksSettingsPage::sslUpdated()
766 {
767     if (_cid && !_cid->sslKey().isNull()) {
768         ui.saslContents->setDisabled(true);
769         ui.saslExtInfo->setHidden(false);
770     } else {
771         ui.saslContents->setDisabled(false);
772         // Directly re-enabling causes the widgets to ignore the parent "Use SASL Authentication"
773         // state to indicate whether or not it's disabled.  To workaround this, keep track of
774         // whether or not "Use SASL Authentication" is enabled, then quickly uncheck/recheck the
775         // group box.
776         if (!ui.sasl->isChecked()) {
777             // SASL is not enabled, uncheck/recheck the group box to re-disable saslContents.
778             // Leaving saslContents disabled doesn't work as that prevents it from re-enabling if
779             // sasl is later checked.
780             ui.sasl->setChecked(true);
781             ui.sasl->setChecked(false);
782         }
783         ui.saslExtInfo->setHidden(true);
784     }
785 }
786 #endif
787
788
789 /*** Network list ***/
790
791 void NetworksSettingsPage::on_networkList_itemSelectionChanged()
792 {
793     if (currentId != 0) {
794         saveToNetworkInfo(networkInfos[currentId]);
795     }
796     if (ui.networkList->selectedItems().count()) {
797         NetworkId id = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
798         currentId = id;
799         displayNetwork(id);
800         ui.serverList->setCurrentRow(0);
801     }
802     else {
803         currentId = 0;
804     }
805     setWidgetStates();
806 }
807
808
809 void NetworksSettingsPage::on_addNetwork_clicked()
810 {
811     QStringList existing;
812     for (int i = 0; i < ui.networkList->count(); i++) existing << ui.networkList->item(i)->text();
813     NetworkAddDlg dlg(existing, this);
814     if (dlg.exec() == QDialog::Accepted) {
815         NetworkInfo info = dlg.networkInfo();
816         if (info.networkName.isEmpty())
817             return;  // sanity check
818
819         NetworkId id;
820         for (id = 1; id <= networkInfos.count(); id++) {
821             widgetHasChanged();
822             if (!networkInfos.keys().contains(-id.toInt())) break;
823         }
824         id = -id.toInt();
825         info.networkId = id;
826         info.identity = defaultIdentity();
827         networkInfos[id] = info;
828         QListWidgetItem *item = insertNetwork(info);
829         ui.networkList->setCurrentItem(item);
830         setWidgetStates();
831     }
832 }
833
834
835 void NetworksSettingsPage::on_deleteNetwork_clicked()
836 {
837     if (ui.networkList->selectedItems().count()) {
838         NetworkId netid = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
839         int ret = QMessageBox::question(this, tr("Delete Network?"),
840             tr("Do you really want to delete the network \"%1\" and all related settings, including the backlog?").arg(networkInfos[netid].networkName),
841             QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
842         if (ret == QMessageBox::Yes) {
843             currentId = 0;
844             networkInfos.remove(netid);
845             delete ui.networkList->takeItem(ui.networkList->row(ui.networkList->selectedItems()[0]));
846             ui.networkList->setCurrentRow(qMin(ui.networkList->currentRow()+1, ui.networkList->count()-1));
847             setWidgetStates();
848             widgetHasChanged();
849         }
850     }
851 }
852
853
854 void NetworksSettingsPage::on_renameNetwork_clicked()
855 {
856     if (!ui.networkList->selectedItems().count()) return;
857     QString old = ui.networkList->selectedItems()[0]->text();
858     QStringList existing;
859     for (int i = 0; i < ui.networkList->count(); i++) existing << ui.networkList->item(i)->text();
860     NetworkEditDlg dlg(old, existing, this);
861     if (dlg.exec() == QDialog::Accepted) {
862         ui.networkList->selectedItems()[0]->setText(dlg.networkName());
863         NetworkId netid = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
864         networkInfos[netid].networkName = dlg.networkName();
865         widgetHasChanged();
866     }
867 }
868
869
870 /*
871 void NetworksSettingsPage::on_connectNow_clicked() {
872   if(!ui.networkList->selectedItems().count()) return;
873   NetworkId id = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
874   const Network *net = Client::network(id);
875   if(!net) return;
876   if(net->connectionState() == Network::Disconnected) net->requestConnect();
877   else net->requestDisconnect();
878 }
879 */
880
881 /*** Server list ***/
882
883 void NetworksSettingsPage::on_serverList_itemSelectionChanged()
884 {
885     setWidgetStates();
886 }
887
888
889 void NetworksSettingsPage::on_addServer_clicked()
890 {
891     if (currentId == 0) return;
892     ServerEditDlg dlg(Network::Server(), this);
893     if (dlg.exec() == QDialog::Accepted) {
894         networkInfos[currentId].serverList.append(dlg.serverData());
895         displayNetwork(currentId);
896         ui.serverList->setCurrentRow(ui.serverList->count()-1);
897         widgetHasChanged();
898     }
899 }
900
901
902 void NetworksSettingsPage::on_editServer_clicked()
903 {
904     if (currentId == 0) return;
905     int cur = ui.serverList->currentRow();
906     ServerEditDlg dlg(networkInfos[currentId].serverList[cur], this);
907     if (dlg.exec() == QDialog::Accepted) {
908         networkInfos[currentId].serverList[cur] = dlg.serverData();
909         displayNetwork(currentId);
910         ui.serverList->setCurrentRow(cur);
911         widgetHasChanged();
912     }
913 }
914
915
916 void NetworksSettingsPage::on_deleteServer_clicked()
917 {
918     if (currentId == 0) return;
919     int cur = ui.serverList->currentRow();
920     networkInfos[currentId].serverList.removeAt(cur);
921     displayNetwork(currentId);
922     ui.serverList->setCurrentRow(qMin(cur, ui.serverList->count()-1));
923     widgetHasChanged();
924 }
925
926
927 void NetworksSettingsPage::on_upServer_clicked()
928 {
929     int cur = ui.serverList->currentRow();
930     Network::Server server = networkInfos[currentId].serverList.takeAt(cur);
931     networkInfos[currentId].serverList.insert(cur-1, server);
932     displayNetwork(currentId);
933     ui.serverList->setCurrentRow(cur-1);
934     widgetHasChanged();
935 }
936
937
938 void NetworksSettingsPage::on_downServer_clicked()
939 {
940     int cur = ui.serverList->currentRow();
941     Network::Server server = networkInfos[currentId].serverList.takeAt(cur);
942     networkInfos[currentId].serverList.insert(cur+1, server);
943     displayNetwork(currentId);
944     ui.serverList->setCurrentRow(cur+1);
945     widgetHasChanged();
946 }
947
948
949 void NetworksSettingsPage::on_editIdentities_clicked()
950 {
951     SettingsPageDlg dlg(new IdentitiesSettingsPage(this), this);
952     dlg.exec();
953 }
954
955
956 void NetworksSettingsPage::on_saslStatusDetails_clicked()
957 {
958     if (ui.networkList->selectedItems().count()) {
959         NetworkId netid = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
960         QString &netName = networkInfos[netid].networkName;
961
962         // If these strings are visible, one of the status messages wasn't detected below.
963         QString saslStatusHeader = "[header unintentionally left blank]";
964         QString saslStatusExplanation = "[explanation unintentionally left blank]";
965
966         // If true, show a warning icon instead of an information icon
967         bool useWarningIcon = false;
968
969         // Determine which explanation to show
970         switch (_saslStatusSelected) {
971         case CapSupportStatus::Unknown:
972             saslStatusHeader = tr("Could not check if SASL supported by network");
973             saslStatusExplanation = tr("Quassel could not check if \"%1\" supports SASL.  This may "
974                                        "be due to unsaved changes or an older Quassel core.  You "
975                                        "can still try using SASL.").arg(netName);
976             break;
977         case CapSupportStatus::Disconnected:
978             saslStatusHeader = tr("Cannot check if SASL supported when disconnected");
979             saslStatusExplanation = tr("Quassel cannot check if \"%1\" supports SASL when "
980                                        "disconnected.  Connect to the network, or try using SASL "
981                                        "anyways.").arg(netName);
982             break;
983         case CapSupportStatus::MaybeUnsupported:
984             saslStatusHeader = tr("SASL not currently supported by network");
985             saslStatusExplanation = tr("The network \"%1\" does not currently support SASL.  "
986                                        "However, support might be added later on.").arg(netName);
987             useWarningIcon = true;
988             break;
989         case CapSupportStatus::MaybeSupported:
990             saslStatusHeader = tr("SASL supported by network");
991             saslStatusExplanation = tr("The network \"%1\" supports SASL.  In most cases, you "
992                                        "should use SASL instead of NickServ identification."
993                                        ).arg(netName);
994             break;
995         }
996
997         // Process this in advance for reusability below
998         const QString saslStatusMsgTitle = tr("SASL support for \"%1\"").arg(netName);
999         const QString saslStatusMsgText =
1000                 QString("<p><b>%1</b></p></br><p>%2</p></br><p><i>%3</i></p>"
1001                         ).arg(saslStatusHeader,
1002                               saslStatusExplanation,
1003                               tr("SASL is a standardized way to log in and identify yourself to "
1004                                  "IRC servers."));
1005
1006         if (useWarningIcon) {
1007             // Show as a warning dialog box
1008             QMessageBox::warning(this, saslStatusMsgTitle, saslStatusMsgText);
1009         } else {
1010             // Show as an information dialog box
1011             QMessageBox::information(this, saslStatusMsgTitle, saslStatusMsgText);
1012         }
1013     }
1014 }
1015
1016
1017 IdentityId NetworksSettingsPage::defaultIdentity() const
1018 {
1019     IdentityId defaultId = 0;
1020     QList<IdentityId> ids = Client::identityIds();
1021     foreach(IdentityId id, ids) {
1022         if (defaultId == 0 || id < defaultId)
1023             defaultId = id;
1024     }
1025     return defaultId;
1026 }
1027
1028
1029 /**************************************************************************
1030 * NetworkAddDlg
1031 *************************************************************************/
1032
1033 NetworkAddDlg::NetworkAddDlg(QStringList exist, QWidget *parent) : QDialog(parent), existing(std::move(exist))
1034 {
1035     ui.setupUi(this);
1036     ui.useSSL->setIcon(icon::get("document-encrypt"));
1037
1038     // Whenever useSSL is toggled, update the port number if not changed from the default
1039     connect(ui.useSSL, SIGNAL(toggled(bool)), SLOT(updateSslPort(bool)));
1040     // Do NOT call updateSslPort when loading settings, otherwise port settings may be overriden.
1041     // If useSSL is later changed to be checked by default, change port's default value, too.
1042
1043     if (Client::isCoreFeatureEnabled(Quassel::Feature::VerifyServerSSL)) {
1044         // Synchronize requiring SSL with the use SSL checkbox
1045         ui.sslVerify->setEnabled(ui.useSSL->isChecked());
1046         connect(ui.useSSL, SIGNAL(toggled(bool)), ui.sslVerify, SLOT(setEnabled(bool)));
1047     } else {
1048         // Core isn't new enough to allow requiring SSL; disable checkbox and uncheck
1049         ui.sslVerify->setEnabled(false);
1050         ui.sslVerify->setChecked(false);
1051         // Split up the message to allow re-using translations:
1052         // [Original tool-tip]
1053         // [Bold 'does not support feature' message]
1054         // [Specific version needed and feature details]
1055         ui.sslVerify->setToolTip(QString("%1<br/><b>%2</b><br/>%3").arg(
1056                                        ui.sslVerify->toolTip(),
1057                                        tr("Your Quassel core does not support this feature"),
1058                                        tr("You need a Quassel core v0.13.0 or newer in order to "
1059                                           "verify connection security.")));
1060     }
1061
1062     // read preset networks
1063     QStringList networks = PresetNetworks::names();
1064     foreach(QString s, existing)
1065     networks.removeAll(s);
1066     if (networks.count())
1067         ui.presetList->addItems(networks);
1068     else {
1069         ui.useManual->setChecked(true);
1070         ui.usePreset->setEnabled(false);
1071     }
1072     connect(ui.networkName, SIGNAL(textChanged(const QString &)), SLOT(setButtonStates()));
1073     connect(ui.serverAddress, SIGNAL(textChanged(const QString &)), SLOT(setButtonStates()));
1074     connect(ui.usePreset, SIGNAL(toggled(bool)), SLOT(setButtonStates()));
1075     connect(ui.useManual, SIGNAL(toggled(bool)), SLOT(setButtonStates()));
1076     setButtonStates();
1077 }
1078
1079
1080 NetworkInfo NetworkAddDlg::networkInfo() const
1081 {
1082     if (ui.useManual->isChecked()) {
1083         NetworkInfo info;
1084         info.networkName = ui.networkName->text().trimmed();
1085         info.serverList << Network::Server(ui.serverAddress->text().trimmed(), ui.port->value(),
1086                                            ui.serverPassword->text(), ui.useSSL->isChecked(),
1087                                            ui.sslVerify->isChecked());
1088         return info;
1089     }
1090     else
1091         return PresetNetworks::networkInfo(ui.presetList->currentText());
1092 }
1093
1094
1095 void NetworkAddDlg::setButtonStates()
1096 {
1097     bool ok = false;
1098     if (ui.usePreset->isChecked() && ui.presetList->count())
1099         ok = true;
1100     else if (ui.useManual->isChecked()) {
1101         ok = !ui.networkName->text().trimmed().isEmpty() && !existing.contains(ui.networkName->text().trimmed())
1102              && !ui.serverAddress->text().isEmpty();
1103     }
1104     ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ok);
1105 }
1106
1107
1108 void NetworkAddDlg::updateSslPort(bool isChecked)
1109 {
1110     // "Use encrypted connection" was toggled, check the state...
1111     if (isChecked && ui.port->value() == Network::PORT_PLAINTEXT) {
1112         // Had been using the plain-text port, use the SSL default
1113         ui.port->setValue(Network::PORT_SSL);
1114     } else if (!isChecked && ui.port->value() == Network::PORT_SSL) {
1115         // Had been using the SSL port, use the plain-text default
1116         ui.port->setValue(Network::PORT_PLAINTEXT);
1117     }
1118 }
1119
1120
1121 /**************************************************************************
1122  * NetworkEditDlg
1123  *************************************************************************/
1124
1125 NetworkEditDlg::NetworkEditDlg(const QString &old, QStringList exist, QWidget *parent) : QDialog(parent), existing(std::move(exist))
1126 {
1127     ui.setupUi(this);
1128
1129     if (old.isEmpty()) {
1130         // new network
1131         setWindowTitle(tr("Add Network"));
1132         on_networkEdit_textChanged(""); // disable ok button
1133     }
1134     else ui.networkEdit->setText(old);
1135 }
1136
1137
1138 QString NetworkEditDlg::networkName() const
1139 {
1140     return ui.networkEdit->text().trimmed();
1141 }
1142
1143
1144 void NetworkEditDlg::on_networkEdit_textChanged(const QString &text)
1145 {
1146     ui.buttonBox->button(QDialogButtonBox::Ok)->setDisabled(text.isEmpty() || existing.contains(text.trimmed()));
1147 }
1148
1149
1150 /**************************************************************************
1151  * ServerEditDlg
1152  *************************************************************************/
1153 ServerEditDlg::ServerEditDlg(const Network::Server &server, QWidget *parent) : QDialog(parent)
1154 {
1155     ui.setupUi(this);
1156     ui.useSSL->setIcon(icon::get("document-encrypt"));
1157     ui.host->setText(server.host);
1158     ui.host->setFocus();
1159     ui.port->setValue(server.port);
1160     ui.password->setText(server.password);
1161     ui.useSSL->setChecked(server.useSsl);
1162     ui.sslVerify->setChecked(server.sslVerify);
1163     ui.sslVersion->setCurrentIndex(server.sslVersion);
1164     ui.useProxy->setChecked(server.useProxy);
1165     ui.proxyType->setCurrentIndex(server.proxyType == QNetworkProxy::Socks5Proxy ? 0 : 1);
1166     ui.proxyHost->setText(server.proxyHost);
1167     ui.proxyPort->setValue(server.proxyPort);
1168     ui.proxyUsername->setText(server.proxyUser);
1169     ui.proxyPassword->setText(server.proxyPass);
1170
1171     // This is a dirty hack to display the core->IRC SSL protocol dropdown
1172     // only if the core won't use autonegotiation to determine the best
1173     // protocol.  When autonegotiation was introduced, it would have been
1174     // a good idea to use the CoreFeatures enum to accomplish this.
1175     // However, since multiple versions have been released since then, that
1176     // is no longer possible.  Instead, we rely on the fact that the
1177     // Datastream protocol was introduced in the same version (0.10) as SSL
1178     // autonegotiation.  Because of that, we can display the dropdown only
1179     // if the Legacy protocol is in use.  If any other RemotePeer protocol
1180     // is in use, that means a newer protocol is in use and therefore the
1181     // core will use autonegotiation.
1182     if (Client::coreConnection()->peer()->protocol() != Protocol::LegacyProtocol) {
1183         ui.label_3->hide();
1184         ui.sslVersion->hide();
1185     }
1186
1187     // Whenever useSSL is toggled, update the port number if not changed from the default
1188     connect(ui.useSSL, SIGNAL(toggled(bool)), SLOT(updateSslPort(bool)));
1189     // Do NOT call updateSslPort when loading settings, otherwise port settings may be overriden.
1190     // If useSSL is later changed to be checked by default, change port's default value, too.
1191
1192     if (Client::isCoreFeatureEnabled(Quassel::Feature::VerifyServerSSL)) {
1193         // Synchronize requiring SSL with the use SSL checkbox
1194         ui.sslVerify->setEnabled(ui.useSSL->isChecked());
1195         connect(ui.useSSL, SIGNAL(toggled(bool)), ui.sslVerify, SLOT(setEnabled(bool)));
1196     } else {
1197         // Core isn't new enough to allow requiring SSL; disable checkbox and uncheck
1198         ui.sslVerify->setEnabled(false);
1199         ui.sslVerify->setChecked(false);
1200         // Split up the message to allow re-using translations:
1201         // [Original tool-tip]
1202         // [Bold 'does not support feature' message]
1203         // [Specific version needed and feature details]
1204         ui.sslVerify->setToolTip(QString("%1<br/><b>%2</b><br/>%3").arg(
1205                                        ui.sslVerify->toolTip(),
1206                                        tr("Your Quassel core does not support this feature"),
1207                                        tr("You need a Quassel core v0.13.0 or newer in order to "
1208                                           "verify connection security.")));
1209     }
1210
1211     on_host_textChanged();
1212 }
1213
1214
1215 Network::Server ServerEditDlg::serverData() const
1216 {
1217     Network::Server server(ui.host->text().trimmed(), ui.port->value(), ui.password->text(),
1218                            ui.useSSL->isChecked(), ui.sslVerify->isChecked());
1219     server.sslVersion = ui.sslVersion->currentIndex();
1220     server.useProxy = ui.useProxy->isChecked();
1221     server.proxyType = ui.proxyType->currentIndex() == 0 ? QNetworkProxy::Socks5Proxy : QNetworkProxy::HttpProxy;
1222     server.proxyHost = ui.proxyHost->text();
1223     server.proxyPort = ui.proxyPort->value();
1224     server.proxyUser = ui.proxyUsername->text();
1225     server.proxyPass = ui.proxyPassword->text();
1226     return server;
1227 }
1228
1229
1230 void ServerEditDlg::on_host_textChanged()
1231 {
1232     ui.buttonBox->button(QDialogButtonBox::Ok)->setDisabled(ui.host->text().trimmed().isEmpty());
1233 }
1234
1235
1236 void ServerEditDlg::updateSslPort(bool isChecked)
1237 {
1238     // "Use encrypted connection" was toggled, check the state...
1239     if (isChecked && ui.port->value() == Network::PORT_PLAINTEXT) {
1240         // Had been using the plain-text port, use the SSL default
1241         ui.port->setValue(Network::PORT_SSL);
1242     } else if (!isChecked && ui.port->value() == Network::PORT_SSL) {
1243         // Had been using the SSL port, use the plain-text default
1244         ui.port->setValue(Network::PORT_PLAINTEXT);
1245     }
1246 }
1247
1248
1249 /**************************************************************************
1250  * SaveNetworksDlg
1251  *************************************************************************/
1252
1253 SaveNetworksDlg::SaveNetworksDlg(const QList<NetworkInfo> &toCreate, const QList<NetworkInfo> &toUpdate, const QList<NetworkId> &toRemove, QWidget *parent) : QDialog(parent)
1254 {
1255     ui.setupUi(this);
1256
1257     numevents = toCreate.count() + toUpdate.count() + toRemove.count();
1258     rcvevents = 0;
1259     if (numevents) {
1260         ui.progressBar->setMaximum(numevents);
1261         ui.progressBar->setValue(0);
1262
1263         connect(Client::instance(), SIGNAL(networkCreated(NetworkId)), this, SLOT(clientEvent()));
1264         connect(Client::instance(), SIGNAL(networkRemoved(NetworkId)), this, SLOT(clientEvent()));
1265
1266         foreach(NetworkId id, toRemove) {
1267             Client::removeNetwork(id);
1268         }
1269         foreach(NetworkInfo info, toCreate) {
1270             Client::createNetwork(info);
1271         }
1272         foreach(NetworkInfo info, toUpdate) {
1273             const Network *net = Client::network(info.networkId);
1274             if (!net) {
1275                 qWarning() << "Invalid client network!";
1276                 numevents--;
1277                 continue;
1278             }
1279             // FIXME this only checks for one changed item rather than all!
1280             connect(net, SIGNAL(updatedRemotely()), this, SLOT(clientEvent()));
1281             Client::updateNetwork(info);
1282         }
1283     }
1284     else {
1285         qWarning() << "Sync dialog called without stuff to change!";
1286         accept();
1287     }
1288 }
1289
1290
1291 void SaveNetworksDlg::clientEvent()
1292 {
1293     ui.progressBar->setValue(++rcvevents);
1294     if (rcvevents >= numevents) accept();
1295 }