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