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