250c0085e71fcf44f51f7bebd2d742695d7ca560
[quassel.git] / src / qtui / coreconnectdlg.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-08 by the Quassel IRC Team                         *
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  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20
21 #include <QDebug>
22 #include <QMessageBox>
23
24 #include "coreconnectdlg.h"
25
26 #include "clientsettings.h"
27 #include "clientsyncer.h"
28
29 CoreConnectDlg::CoreConnectDlg(QWidget *parent, bool autoconnect) : QDialog(parent) {
30   ui.setupUi(this);
31
32   clientSyncer = new ClientSyncer(this);
33
34   setAttribute(Qt::WA_DeleteOnClose);
35
36   doingAutoConnect = false;
37
38   ui.stackedWidget->setCurrentWidget(ui.accountPage);
39   ui.accountButtonBox->setFocus();
40   ui.accountButtonBox->button(QDialogButtonBox::Ok)->setDefault(true);
41
42   CoreAccountSettings s;
43   AccountId lastacc = s.lastAccount();
44   autoConnectAccount = s.autoConnectAccount();
45   QListWidgetItem *currentItem = 0;
46   foreach(AccountId id, s.knownAccounts()) {
47     if(!id.isValid()) continue;
48     QVariantMap data = s.retrieveAccountData(id);
49     accounts[id] = data;
50     QListWidgetItem *item = new QListWidgetItem(data["AccountName"].toString(), ui.accountList);
51     item->setData(Qt::UserRole, QVariant::fromValue<AccountId>(id));
52     if(id == lastacc) currentItem = item;
53   }
54   if(currentItem) ui.accountList->setCurrentItem(currentItem);
55   else ui.accountList->setCurrentRow(0);
56
57   setAccountWidgetStates();
58
59   connect(clientSyncer, SIGNAL(socketStateChanged(QAbstractSocket::SocketState)),this, SLOT(initPhaseSocketState(QAbstractSocket::SocketState)));
60   connect(clientSyncer, SIGNAL(connectionError(const QString &)), this, SLOT(initPhaseError(const QString &)));
61   connect(clientSyncer, SIGNAL(connectionMsg(const QString &)), this, SLOT(initPhaseMsg(const QString &)));
62   connect(clientSyncer, SIGNAL(startLogin()), this, SLOT(startLogin()));
63   connect(clientSyncer, SIGNAL(loginFailed(const QString &)), this, SLOT(loginFailed(const QString &)));
64   connect(clientSyncer, SIGNAL(loginSuccess()), this, SLOT(startSync()));
65   connect(clientSyncer, SIGNAL(sessionProgress(quint32, quint32)), this, SLOT(coreSessionProgress(quint32, quint32)));
66   connect(clientSyncer, SIGNAL(networksProgress(quint32, quint32)), this, SLOT(coreNetworksProgress(quint32, quint32)));
67   connect(clientSyncer, SIGNAL(channelsProgress(quint32, quint32)), this, SLOT(coreChannelsProgress(quint32, quint32)));
68   connect(clientSyncer, SIGNAL(ircUsersProgress(quint32, quint32)), this, SLOT(coreIrcUsersProgress(quint32, quint32)));
69   connect(clientSyncer, SIGNAL(syncFinished()), this, SLOT(accept()));
70
71   connect(ui.user, SIGNAL(textChanged(const QString &)), this, SLOT(setLoginWidgetStates()));
72   connect(ui.password, SIGNAL(textChanged(const QString &)), this, SLOT(setLoginWidgetStates()));
73
74   connect(ui.loginButtonBox, SIGNAL(rejected()), this, SLOT(restartPhaseNull()));
75   connect(ui.syncButtonBox->button(QDialogButtonBox::Abort), SIGNAL(clicked()), this, SLOT(restartPhaseNull()));
76
77   if(autoconnect && ui.accountList->count() && autoConnectAccount.isValid()
78      && autoConnectAccount == ui.accountList->currentItem()->data(Qt::UserRole).value<AccountId>()) {
79     doingAutoConnect = true;
80     on_accountButtonBox_accepted();
81   }
82 }
83
84 CoreConnectDlg::~CoreConnectDlg() {
85   if(ui.accountList->selectedItems().count()) {
86     CoreAccountSettings s;
87     s.setLastAccount(ui.accountList->selectedItems()[0]->data(Qt::UserRole).value<AccountId>());
88   }
89 }
90
91
92 /****************************************************
93  * Account Management
94  ***************************************************/
95
96 void CoreConnectDlg::on_accountList_itemSelectionChanged() {
97   setAccountWidgetStates();
98 }
99
100 void CoreConnectDlg::setAccountWidgetStates() {
101   QList<QListWidgetItem *> selectedItems = ui.accountList->selectedItems();
102   ui.editAccount->setEnabled(selectedItems.count());
103   ui.deleteAccount->setEnabled(selectedItems.count());
104   ui.autoConnect->setEnabled(selectedItems.count());
105   if(selectedItems.count()) {
106     ui.autoConnect->setChecked(selectedItems[0]->data(Qt::UserRole).value<AccountId>() == autoConnectAccount);
107   }
108   ui.accountButtonBox->button(QDialogButtonBox::Ok)->setEnabled(ui.accountList->count());
109 }
110
111 void CoreConnectDlg::on_autoConnect_clicked(bool state) {
112   if(!state) {
113     autoConnectAccount = 0;
114   } else {
115     if(ui.accountList->selectedItems().count()) {
116       autoConnectAccount = ui.accountList->selectedItems()[0]->data(Qt::UserRole).value<AccountId>();
117     } else {
118       qWarning() << "Checked auto connect without an enabled item!";  // should never happen!
119       autoConnectAccount = 0;
120     }
121   }
122   setAccountWidgetStates();
123 }
124
125 void CoreConnectDlg::on_addAccount_clicked() {
126   QStringList existing;
127   for(int i = 0; i < ui.accountList->count(); i++) existing << ui.accountList->item(i)->text();
128   CoreAccountEditDlg dlg(0, QVariantMap(), existing, this);
129   if(dlg.exec() == QDialog::Accepted) {
130     // find free ID
131     AccountId id = accounts.count() + 1;
132     for(AccountId i = 1; i <= accounts.count(); i++) {
133       if(!accounts.keys().contains(i)) {
134         id = i;
135         break;
136       }
137     }
138     QVariantMap data = dlg.accountData();
139     data["AccountId"] = QVariant::fromValue<AccountId>(id);
140     accounts[id] = data;
141     QListWidgetItem *item = new QListWidgetItem(data["AccountName"].toString(), ui.accountList);
142     item->setData(Qt::UserRole, QVariant::fromValue<AccountId>(id));
143     ui.accountList->setCurrentItem(item);
144   }
145 }
146
147 void CoreConnectDlg::on_editAccount_clicked() {
148   QStringList existing;
149   for(int i = 0; i < ui.accountList->count(); i++) existing << ui.accountList->item(i)->text();
150   AccountId id = ui.accountList->currentItem()->data(Qt::UserRole).value<AccountId>();
151   QVariantMap acct = accounts[id];
152   CoreAccountEditDlg dlg(id, acct, existing, this);
153   if(dlg.exec() == QDialog::Accepted) {
154     QVariantMap data = dlg.accountData();
155     ui.accountList->currentItem()->setText(data["AccountName"].toString());
156     accounts[id] = data;
157   }
158 }
159
160 void CoreConnectDlg::on_deleteAccount_clicked() {
161   AccountId id = ui.accountList->currentItem()->data(Qt::UserRole).value<AccountId>();
162   int ret = QMessageBox::question(this, tr("Remove Account Settings"),
163                                   tr("Do you really want to remove your local settings for this Quassel Core account?<br>"
164                                   "Note: This will <em>not</em> remove or change any data on the Core itself!"),
165                                   QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
166   if(ret == QMessageBox::Yes) {
167     int idx = ui.accountList->currentRow();
168     delete ui.accountList->takeItem(idx);
169     ui.accountList->setCurrentRow(qMin(idx, ui.accountList->count()-1));
170     accounts[id]["Delete"] = true;  // we only flag this here, actual deletion happens on accept!
171     setAccountWidgetStates();
172   }
173 }
174
175 void CoreConnectDlg::on_accountList_itemDoubleClicked(QListWidgetItem *item) {
176   Q_UNUSED(item);
177   on_accountButtonBox_accepted();
178 }
179
180 void CoreConnectDlg::on_accountButtonBox_accepted() {
181   // save accounts
182   CoreAccountSettings s;
183   foreach(QVariantMap acct, accounts.values()) {
184     AccountId id = acct["AccountId"].value<AccountId>();
185     if(acct.contains("Delete")) {
186       s.removeAccount(id);
187     } else {
188       s.storeAccountData(id, acct);
189     }
190   }
191   s.setAutoConnectAccount(autoConnectAccount);
192
193   ui.stackedWidget->setCurrentWidget(ui.loginPage);
194   account = ui.accountList->currentItem()->data(Qt::UserRole).value<AccountId>();
195   accountData = accounts[account];
196   s.setLastAccount(account);
197   connectToCore();
198 }
199
200 /*****************************************************
201  * Connecting to the Core
202  ****************************************************/
203
204 /*** Phase One: initializing the core connection ***/
205
206 void CoreConnectDlg::connectToCore() {
207   ui.connectIcon->setPixmap(QPixmap::fromImage(QImage(":/22x22/actions/network-disconnect")));
208   ui.connectLabel->setText(tr("Connect to %1").arg(accountData["Host"].toString()));
209   ui.coreInfoLabel->setText("");
210   ui.loginStack->setCurrentWidget(ui.loginEmptyPage);
211   ui.loginButtonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
212   ui.loginButtonBox->button(QDialogButtonBox::Ok)->setDefault(true);
213   ui.loginButtonBox->button(QDialogButtonBox::Ok)->setDisabled(true);
214   disconnect(ui.loginButtonBox, 0, this, 0);
215   connect(ui.loginButtonBox, SIGNAL(rejected()), this, SLOT(restartPhaseNull()));
216
217
218   //connect(Client::instance(), SIGNAL(coreConnectionPhaseOne(const QVariantMap &)), this, SLOT(phaseOneFinished
219   clientSyncer->connectToCore(accountData);
220 }
221
222 void CoreConnectDlg::initPhaseError(const QString &error) {
223   doingAutoConnect = false;
224   ui.connectIcon->setPixmap(QPixmap::fromImage(QImage(":/22x22/status/dialog-error")));
225   //ui.connectLabel->setBrush(QBrush("red"));
226   ui.connectLabel->setText(tr("<div style=color:red;>Connection to %1 failed!</div>").arg(accountData["Host"].toString()));
227   ui.coreInfoLabel->setText(error);
228   ui.loginButtonBox->setStandardButtons(QDialogButtonBox::Retry|QDialogButtonBox::Cancel);
229   ui.loginButtonBox->button(QDialogButtonBox::Retry)->setDefault(true);
230   disconnect(ui.loginButtonBox, 0, this, 0);
231   connect(ui.loginButtonBox, SIGNAL(accepted()), this, SLOT(restartPhaseNull()));
232   connect(ui.loginButtonBox, SIGNAL(rejected()), this, SLOT(reject()));
233 }
234
235 void CoreConnectDlg::initPhaseMsg(const QString &msg) {
236   ui.coreInfoLabel->setText(msg);
237 }
238
239 void CoreConnectDlg::initPhaseSocketState(QAbstractSocket::SocketState state) {
240   QString s;
241   QString host = accountData["Host"].toString();
242   switch(state) {
243     case QAbstractSocket::UnconnectedState: s = tr("Not connected to %1.").arg(host); break;
244     case QAbstractSocket::HostLookupState: s = tr("Looking up %1...").arg(host); break;
245     case QAbstractSocket::ConnectingState: s = tr("Connecting to %1...").arg(host); break;
246     case QAbstractSocket::ConnectedState: s = tr("Connected to %1").arg(host); break;
247     default: s = tr("Unknown connection state to %1"); break;
248   }
249   ui.connectLabel->setText(s);
250 }
251
252 void CoreConnectDlg::restartPhaseNull() {
253   doingAutoConnect = false;
254   ui.stackedWidget->setCurrentWidget(ui.accountPage);
255   clientSyncer->disconnectFromCore();
256 }
257
258 /*********************************************************
259  * Phase Two: Login
260  *********************************************************/
261
262 void CoreConnectDlg::startLogin() {
263   ui.connectIcon->setPixmap(QPixmap::fromImage(QImage(":/22x22/actions/network-connect")));
264   ui.loginStack->setCurrentWidget(ui.loginCredentialsPage);
265   ui.loginStack->setMinimumSize(ui.loginStack->sizeHint()); ui.loginStack->updateGeometry();
266   ui.loginButtonBox->setStandardButtons(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
267   ui.loginButtonBox->button(QDialogButtonBox::Ok)->setDefault(true);
268   if(!accountData["User"].toString().isEmpty()) {
269     ui.user->setText(accountData["User"].toString());
270     if(accountData["RememberPasswd"].toBool()) {
271       ui.password->setText(accountData["Password"].toString());
272       ui.rememberPasswd->setChecked(true);
273       ui.loginButtonBox->setFocus();
274     } else {
275       ui.rememberPasswd->setChecked(false);
276       ui.password->setFocus();
277     }
278   } else ui.user->setFocus();
279   disconnect(ui.loginButtonBox, 0, this, 0);
280   connect(ui.loginButtonBox, SIGNAL(accepted()), this, SLOT(doLogin()));
281   connect(ui.loginButtonBox, SIGNAL(rejected()), this, SLOT(restartPhaseNull()));
282   if(doingAutoConnect) doLogin();
283 }
284
285 void CoreConnectDlg::doLogin() {
286   ui.loginGroup->setTitle(tr("Logging in..."));
287   ui.user->setDisabled(true);
288   ui.password->setDisabled(true);
289   ui.rememberPasswd->setDisabled(true);
290   ui.loginButtonBox->button(QDialogButtonBox::Ok)->setDisabled(true);
291   accountData["User"] = ui.user->text();
292   accountData["RememberPasswd"] = ui.rememberPasswd->isChecked();
293   if(ui.rememberPasswd->isChecked()) accountData["Password"] = ui.password->text();
294   else accountData.remove("Password");
295   CoreAccountSettings s;
296   s.storeAccountData(account, accountData);
297   clientSyncer->loginToCore(ui.user->text(), ui.password->text());
298 }
299
300 void CoreConnectDlg::setLoginWidgetStates() {
301   ui.loginButtonBox->button(QDialogButtonBox::Ok)->setDisabled(ui.user->text().isEmpty() || ui.password->text().isEmpty());
302 }
303
304 void CoreConnectDlg::loginFailed(const QString &error) {
305   ui.loginGroup->setTitle(tr("Login"));
306   ui.user->setEnabled(true);
307   ui.password->setEnabled(true);
308   ui.rememberPasswd->setEnabled(true);
309   ui.coreInfoLabel->setText(error);
310   ui.loginButtonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
311   ui.password->setFocus();
312   doingAutoConnect = false;
313 }
314
315 /************************************************************
316  * Phase Three: Syncing
317  ************************************************************/
318
319 void CoreConnectDlg::startSync() {
320   ui.sessionProgress->setRange(0, 1);
321   ui.sessionProgress->setValue(0);
322   ui.networksProgress->setRange(0, 1);
323   ui.networksProgress->setValue(0);
324   ui.channelsProgress->setRange(0, 1);
325   ui.channelsProgress->setValue(0);
326   ui.ircUsersProgress->setRange(0, 1);
327   ui.ircUsersProgress->setValue(0);
328
329   ui.stackedWidget->setCurrentWidget(ui.syncPage);
330   // clean up old page
331   ui.loginGroup->setTitle(tr("Login"));
332   ui.user->setEnabled(true);
333   ui.password->setEnabled(true);
334   ui.rememberPasswd->setEnabled(true);
335   ui.loginButtonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
336 }
337
338
339 void CoreConnectDlg::coreSessionProgress(quint32 val, quint32 max) {
340   ui.sessionProgress->setRange(0, max);
341   ui.sessionProgress->setValue(val);
342
343 }
344
345 void CoreConnectDlg::coreNetworksProgress(quint32 val, quint32 max) {
346   if(max == 0) {
347     ui.networksProgress->setFormat("0/0");
348     ui.networksProgress->setRange(0, 1);
349     ui.networksProgress->setValue(1);
350   } else {
351     ui.networksProgress->setFormat("%v/%m");
352     ui.networksProgress->setRange(0, max);
353     ui.networksProgress->setValue(val);
354   }
355 }
356
357 void CoreConnectDlg::coreChannelsProgress(quint32 val, quint32 max) {
358   if(max == 0) {
359     ui.channelsProgress->setFormat("0/0");
360     ui.channelsProgress->setRange(0, 1);
361     ui.channelsProgress->setValue(1);
362   } else {
363     ui.channelsProgress->setFormat("%v/%m");
364     ui.channelsProgress->setRange(0, max);
365     ui.channelsProgress->setValue(val);
366   }
367 }
368
369 void CoreConnectDlg::coreIrcUsersProgress(quint32 val, quint32 max) {
370   if(max == 0) {
371     ui.ircUsersProgress->setFormat("0/0");
372     ui.ircUsersProgress->setRange(0, 1);
373     ui.ircUsersProgress->setValue(1);
374   } else {
375     if(val % 100) return;
376     ui.ircUsersProgress->setFormat("%v/%m");
377     ui.ircUsersProgress->setRange(0, max);
378     ui.ircUsersProgress->setValue(val);
379   }
380 }
381
382 /*****************************************************************************************
383  * CoreAccountEditDlg
384  *****************************************************************************************/
385
386 CoreAccountEditDlg::CoreAccountEditDlg(AccountId id, const QVariantMap &acct, const QStringList &_existing, QWidget *parent) : QDialog(parent) {
387   ui.setupUi(this);
388   existing = _existing;
389   account = acct;
390   if(id.isValid()) {
391     existing.removeAll(acct["AccountName"].toString());
392     ui.host->setText(acct["Host"].toString());
393     ui.port->setValue(acct["Port"].toUInt());
394     ui.useInternal->setChecked(acct["UseInternal"].toBool());
395     ui.accountName->setText(acct["AccountName"].toString());
396   } else {
397     setWindowTitle(tr("Add Core Account"));
398   }
399 }
400
401 QVariantMap CoreAccountEditDlg::accountData() {
402   account["AccountName"] = ui.accountName->text();
403   account["Host"] = ui.host->text();
404   account["Port"] = ui.port->value();
405   account["UseInternal"] = ui.useInternal->isChecked();
406   return account;
407 }
408
409 void CoreAccountEditDlg::setWidgetStates() {
410   bool ok = !ui.accountName->text().isEmpty() && !existing.contains(ui.accountName->text()) && (ui.useInternal->isChecked() || !ui.host->text().isEmpty());
411   ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ok);
412 }
413
414 void CoreAccountEditDlg::on_host_textChanged(const QString &text) {
415   Q_UNUSED(text);
416   setWidgetStates();
417 }
418
419 void CoreAccountEditDlg::on_accountName_textChanged(const QString &text) {
420   Q_UNUSED(text);
421   setWidgetStates();
422 }
423
424 void CoreAccountEditDlg::on_useRemote_toggled(bool state) {
425   Q_UNUSED(state);
426   setWidgetStates();
427 }