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