c9cb1f122477dc18952448718b70621b964c7383
[quassel.git] / src / qtui / settingspages / identitiessettingspage.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-08 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) any later version.                                   *
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 <QInputDialog>
22 #include <QMessageBox>
23
24 #include "identitiessettingspage.h"
25
26 #include "client.h"
27
28 IdentitiesSettingsPage::IdentitiesSettingsPage(QWidget *parent)
29   : SettingsPage(tr("General"), tr("Identities"), parent) {
30
31   ui.setupUi(this);
32   setEnabled(false);  // need a core connection!
33   connect(Client::instance(), SIGNAL(coreConnectionStateChanged(bool)), this, SLOT(coreConnectionStateChanged(bool)));
34   connect(Client::instance(), SIGNAL(identityCreated(IdentityId)), this, SLOT(clientIdentityCreated(IdentityId)));
35   connect(Client::instance(), SIGNAL(identityRemoved(IdentityId)), this, SLOT(clientIdentityRemoved(IdentityId)));
36
37   currentId = 0;
38
39   // We need to know whenever the state of input widgets changes...
40   //connect(ui.identityList, SIGNAL(editTextChanged(const QString &)), this, SLOT(widgetHasChanged()));
41   connect(ui.realName, SIGNAL(textEdited(const QString &)), this, SLOT(widgetHasChanged()));
42   connect(ui.nicknameList, SIGNAL(itemChanged(QListWidgetItem *)), this, SLOT(widgetHasChanged()));
43   connect(ui.awayNick, SIGNAL(textEdited(const QString &)), this, SLOT(widgetHasChanged()));
44   connect(ui.awayNickEnabled, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
45   connect(ui.awayReason, SIGNAL(textEdited(const QString &)), this, SLOT(widgetHasChanged()));
46   connect(ui.awayReasonEnabled, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
47   connect(ui.returnMessage, SIGNAL(textEdited(const QString &)), this, SLOT(widgetHasChanged()));
48   connect(ui.returnMessageEnabled, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
49   connect(ui.autoAwayEnabled, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
50   connect(ui.autoAwayTime, SIGNAL(valueChanged(int)), this, SLOT(widgetHasChanged()));
51   connect(ui.autoAwayReason, SIGNAL(textEdited(const QString &)), this, SLOT(widgetHasChanged()));
52   connect(ui.autoAwayReasonEnabled, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
53   connect(ui.autoReturnMessage, SIGNAL(textEdited(const QString &)), this, SLOT(widgetHasChanged()));
54   connect(ui.autoReturnMessageEnabled, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
55   connect(ui.ident, SIGNAL(textEdited(const QString &)), this, SLOT(widgetHasChanged()));
56   connect(ui.kickReason, SIGNAL(textEdited(const QString &)), this, SLOT(widgetHasChanged()));
57   connect(ui.partReason, SIGNAL(textEdited(const QString &)), this, SLOT(widgetHasChanged()));
58   connect(ui.quitReason, SIGNAL(textEdited(const QString &)), this, SLOT(widgetHasChanged()));
59
60 }
61
62 void IdentitiesSettingsPage::coreConnectionStateChanged(bool state) {
63   this->setEnabled(state);
64   if(state) {
65     load();
66   } else {
67     // reset
68     currentId = 0;
69   }
70 }
71
72 void IdentitiesSettingsPage::save() {
73   setEnabled(false);
74   QList<Identity *> toCreate, toUpdate;
75   // we need to remove our temporarily created identities.
76   // these are going to be re-added after the core has propagated them back...
77   QHash<IdentityId, Identity *>::iterator i = identities.begin();
78   while(i != identities.end()) {
79     if((*i)->id() < 0) {
80       Identity *temp = *i;
81       i = identities.erase(i);
82       toCreate.append(temp);
83       ui.identityList->removeItem(ui.identityList->findData(temp->id()));
84     } else {
85       if(**i != *Client::identity((*i)->id())) {
86         toUpdate.append(*i);
87       }
88       ++i;
89     }
90   }
91   SaveIdentitiesDlg dlg(toCreate, toUpdate, deletedIdentities, this);
92   int ret = dlg.exec();
93   if(ret == QDialog::Rejected) {
94     // canceled -> reload everything to be safe
95     load();
96   }
97   foreach(Identity *id, toCreate) {
98     id->deleteLater();
99   }
100   changedIdentities.clear();
101   deletedIdentities.clear();
102   changeState(false);
103   setEnabled(true);
104 }
105
106 void IdentitiesSettingsPage::load() {
107   currentId = 0;
108   foreach(Identity *identity, identities.values()) {
109     identity->deleteLater();
110   }
111   identities.clear();
112   deletedIdentities.clear();
113   changedIdentities.clear();
114   ui.identityList->clear();
115   foreach(IdentityId id, Client::identityIds()) {
116     clientIdentityCreated(id);
117   }
118   changeState(false);
119 }
120
121 void IdentitiesSettingsPage::defaults() {
122   // TODO implement bool hasDefaults()
123 }
124
125 void IdentitiesSettingsPage::widgetHasChanged() {
126   bool changed = testHasChanged();
127   if(changed != hasChanged()) changeState(changed);
128 }
129
130 bool IdentitiesSettingsPage::testHasChanged() {
131   if(deletedIdentities.count()) return true;
132   if(currentId < 0) {
133     return true; // new identity
134   } else {
135     if(currentId != 0) {
136       changedIdentities.removeAll(currentId);
137       Identity temp(currentId, this);
138       saveToIdentity(&temp);
139       temp.setIdentityName(identities[currentId]->identityName());
140       if(temp != *Client::identity(currentId)) changedIdentities.append(currentId);
141     }
142     return changedIdentities.count();
143   }
144 }
145
146 bool IdentitiesSettingsPage::aboutToSave() {
147   saveToIdentity(identities[currentId]);
148   QList<int> errors;
149   foreach(Identity *id, identities.values()) {
150     if(id->identityName().isEmpty()) errors.append(1);
151     if(!id->nicks().count()) errors.append(2);
152     if(id->realName().isEmpty()) errors.append(3);
153     if(id->ident().isEmpty()) errors.append(4);
154   }
155   if(!errors.count()) return true;
156   QString error(tr("<b>The following problems need to be corrected before your changes can be applied:</b><ul>"));
157   if(errors.contains(1)) error += tr("<li>All identities need an identity name set</li>");
158   if(errors.contains(2)) error += tr("<li>Every identity needs at least one nickname defined</li>");
159   if(errors.contains(3)) error += tr("<li>You need to specify a real name for every identity</li>");
160   if(errors.contains(4)) error += tr("<li>You need to specify an ident for every identity</li>");
161   error += tr("</ul>");
162   QMessageBox::warning(this, tr("One or more identities are invalid"), error);
163   return false;
164 }
165
166 void IdentitiesSettingsPage::clientIdentityCreated(IdentityId id) {
167   insertIdentity(new Identity(*Client::identity(id), this));
168   Identity *i = identities[id];
169   connect(Client::identity(id), SIGNAL(updatedRemotely()), this, SLOT(clientIdentityUpdated()));
170 }
171
172 void IdentitiesSettingsPage::clientIdentityUpdated() {
173   const Identity *clientIdentity = qobject_cast<Identity *>(sender());
174   if(!clientIdentity) {
175     qWarning() << "Invalid identity to update!";
176     return;
177   }
178   if(!identities.contains(clientIdentity->id())) {
179     qWarning() << "Unknown identity to update:" << clientIdentity->identityName();
180     return;
181   }
182   Identity *identity = identities[clientIdentity->id()];
183   if(identity->identityName() != clientIdentity->identityName()) renameIdentity(identity->id(), clientIdentity->identityName());
184   identity->update(*clientIdentity);
185   if(identity->id() == currentId) displayIdentity(identity, true);
186 }
187
188 void IdentitiesSettingsPage::clientIdentityRemoved(IdentityId id) {
189   if(identities.contains(id)) {
190     removeIdentity(identities[id]);
191     changedIdentities.removeAll(id);
192     deletedIdentities.removeAll(id);
193   }
194 }
195
196 void IdentitiesSettingsPage::insertIdentity(Identity *identity) {
197   IdentityId id = identity->id();
198   identities[id] = identity;
199   if(id == 1) {
200     // default identity is always the first one!
201     ui.identityList->insertItem(0, identity->identityName(), id);
202   } else {
203     QString name = identity->identityName();
204     for(int j = 0; j < ui.identityList->count(); j++) {
205       if((j>0 || ui.identityList->itemData(0).toInt() != 1) && name.localeAwareCompare(ui.identityList->itemText(j)) < 0) {
206         ui.identityList->insertItem(j, name, id);
207         widgetHasChanged();
208         return;
209       }
210     }
211     // append
212     ui.identityList->insertItem(ui.identityList->count(), name, id);
213     widgetHasChanged();
214   }
215 }
216
217 void IdentitiesSettingsPage::renameIdentity(IdentityId id, const QString &newName) {
218   Identity *identity = identities[id];
219   ui.identityList->setItemText(ui.identityList->findData(identity->id()), newName);
220   identity->setIdentityName(newName);
221 }
222
223 void IdentitiesSettingsPage::removeIdentity(Identity *id) {
224   identities.remove(id->id());
225   ui.identityList->removeItem(ui.identityList->findData(id->id()));
226   changedIdentities.removeAll(id->id());
227   id->deleteLater();
228   widgetHasChanged();
229 }
230
231 void IdentitiesSettingsPage::on_identityList_currentIndexChanged(int index) {
232   if(index < 0) {
233     //ui.identityList->setEditable(false);
234     displayIdentity(0);
235   } else {
236     IdentityId id = ui.identityList->itemData(index).toInt();
237     if(identities.contains(id)) displayIdentity(identities[id]);
238     ui.deleteIdentity->setEnabled(id != 1); // default identity cannot be deleted
239     ui.renameIdentity->setEnabled(id != 1); // ...or renamed
240   }
241 }
242
243 void IdentitiesSettingsPage::displayIdentity(Identity *id, bool dontsave) {
244   if(currentId != 0 && !dontsave && identities.contains(currentId)) {
245     saveToIdentity(identities[currentId]);
246   }
247   if(id) {
248     currentId = id->id();
249     ui.realName->setText(id->realName());
250     ui.nicknameList->clear();
251     ui.nicknameList->addItems(id->nicks());
252     //for(int i = 0; i < ui.nicknameList->count(); i++) {
253     //  ui.nicknameList->item(i)->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEditable|Qt::ItemIsEnabled);
254     //}
255     if(ui.nicknameList->count()) ui.nicknameList->setCurrentRow(0);
256     ui.awayNick->setText(id->awayNick());
257     ui.awayNickEnabled->setChecked(id->awayNickEnabled());
258     ui.awayReason->setText(id->awayReason());
259     ui.awayReasonEnabled->setChecked(id->awayReasonEnabled());
260     ui.returnMessage->setText(id->returnMessage());
261     ui.returnMessageEnabled->setChecked(id->returnMessageEnabled());
262     ui.autoAwayEnabled->setChecked(id->autoAwayEnabled());
263     ui.autoAwayTime->setValue(id->autoAwayTime());
264     ui.autoAwayReason->setText(id->autoAwayReason());
265     ui.autoAwayReasonEnabled->setChecked(id->autoAwayReasonEnabled());
266     ui.autoReturnMessage->setText(id->autoReturnMessage());
267     ui.autoReturnMessageEnabled->setChecked(id->autoReturnMessageEnabled());
268     ui.ident->setText(id->ident());
269     ui.kickReason->setText(id->kickReason());
270     ui.partReason->setText(id->partReason());
271     ui.quitReason->setText(id->quitReason());
272   }
273 }
274
275 void IdentitiesSettingsPage::saveToIdentity(Identity *id) {
276   id->setRealName(ui.realName->text());
277   QStringList nicks;
278   for(int i = 0; i < ui.nicknameList->count(); i++) {
279     nicks << ui.nicknameList->item(i)->text();
280   }
281   id->setNicks(nicks);
282   id->setAwayNick(ui.awayNick->text());
283   id->setAwayNickEnabled(ui.awayNickEnabled->isChecked());
284   id->setAwayReason(ui.awayReason->text());
285   id->setAwayReasonEnabled(ui.awayReasonEnabled->isChecked());
286   id->setReturnMessage(ui.returnMessage->text());
287   id->setReturnMessageEnabled(ui.returnMessageEnabled->isChecked());
288   id->setAutoAwayEnabled(ui.autoAwayEnabled->isChecked());
289   id->setAutoAwayTime(ui.autoAwayTime->value());
290   id->setAutoAwayReason(ui.autoAwayReason->text());
291   id->setAutoAwayReasonEnabled(ui.autoAwayReasonEnabled->isChecked());
292   id->setAutoReturnMessage(ui.autoReturnMessage->text());
293   id->setAutoReturnMessageEnabled(ui.autoReturnMessageEnabled->isChecked());
294   id->setIdent(ui.ident->text());
295   id->setKickReason(ui.kickReason->text());
296   id->setPartReason(ui.partReason->text());
297   id->setQuitReason(ui.quitReason->text());
298 }
299
300 void IdentitiesSettingsPage::on_addIdentity_clicked() {
301   CreateIdentityDlg dlg(ui.identityList->model(), this);
302   if(dlg.exec() == QDialog::Accepted) {
303     // find a free (negative) ID
304     IdentityId id;
305     for(id = 1; id <= identities.count(); id++) {
306       if(!identities.keys().contains(-id)) break;
307     }
308     id *= -1;
309     Identity *newId = new Identity(id, this);
310     if(dlg.duplicateId() != 0) {
311       // duplicate
312       newId->update(*identities[dlg.duplicateId()]);
313       newId->setId(id);
314     }
315     newId->setIdentityName(dlg.identityName());
316     identities[id] = newId;
317     insertIdentity(newId);
318     ui.identityList->setCurrentIndex(ui.identityList->findData(id));
319     widgetHasChanged();
320   }
321 }
322
323 void IdentitiesSettingsPage::on_deleteIdentity_clicked() {
324   Identity *id = identities[currentId];
325   int ret = QMessageBox::question(this, tr("Delete Identity?"),
326                                   tr("Do you really want to delete identity \"%1\"?").arg(id->identityName()),
327                                   QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
328   if(ret != QMessageBox::Yes) return;
329   if(id->id() > 0) deletedIdentities.append(id->id());
330   currentId = 0;
331   removeIdentity(id);
332 }
333
334 void IdentitiesSettingsPage::on_renameIdentity_clicked() {
335   QString oldName = identities[currentId]->identityName();
336   bool ok = false;
337   QString name = QInputDialog::getText(this, tr("Rename Identity"),
338                                        tr("Please enter a new name for the identity \"%1\":").arg(oldName),
339                                        QLineEdit::Normal, oldName, &ok);
340   if(ok && !name.isEmpty()) {
341     renameIdentity(currentId, name);
342     widgetHasChanged();
343   }
344 }
345
346 /*****************************************************************************************/
347
348 CreateIdentityDlg::CreateIdentityDlg(QAbstractItemModel *model, QWidget *parent) : QDialog(parent) {
349   ui.setupUi(this);
350
351   ui.identityList->setModel(model);  // now we use the identity list of the main page... Trolltech <3
352   on_identityName_textChanged("");   // disable ok button :)
353 }
354
355 QString CreateIdentityDlg::identityName() const {
356   return ui.identityName->text();
357 }
358
359 IdentityId CreateIdentityDlg::duplicateId() const {
360   if(!ui.duplicateIdentity->isChecked()) return 0;
361   if(ui.identityList->currentIndex() >= 0) {
362     return ui.identityList->itemData(ui.identityList->currentIndex()).toInt();
363   }
364   return 0;
365 }
366
367 void CreateIdentityDlg::on_identityName_textChanged(const QString &text) {
368   ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(text.count());
369
370 }
371
372 /*********************************************************************************************/
373
374 SaveIdentitiesDlg::SaveIdentitiesDlg(QList<Identity *> tocreate, QList<Identity *> toupdate, QList<IdentityId> toremove, QWidget *parent)
375   : QDialog(parent), toCreate(tocreate), toUpdate(toupdate), toRemove(toremove) {
376   ui.setupUi(this);
377   numevents = toCreate.count() + toUpdate.count() + toRemove.count();
378   rcvevents = 0;
379   if(numevents) {
380     ui.progressBar->setMaximum(numevents);
381     ui.progressBar->setValue(0);
382
383     connect(Client::instance(), SIGNAL(identityCreated(IdentityId)), this, SLOT(clientEvent()));
384     connect(Client::instance(), SIGNAL(identityRemoved(IdentityId)), this, SLOT(clientEvent()));
385
386     foreach(Identity *id, toCreate) {
387       Client::createIdentity(*id);
388     }
389     foreach(Identity *id, toUpdate) {
390       const Identity *cid = Client::identity(id->id());
391       if(!cid) {
392         qWarning() << "Invalid client identity!";
393         numevents--;
394         continue;
395       }
396       connect(cid, SIGNAL(updatedRemotely()), this, SLOT(clientEvent()));
397       Client::updateIdentity(*id);
398     }
399     foreach(IdentityId id, toRemove) {
400       Client::removeIdentity(id);
401     }
402   } else {
403     qWarning() << "Sync dialog called without stuff to change!";
404     accept();
405   }
406 }
407
408 void SaveIdentitiesDlg::clientEvent() {
409   ui.progressBar->setValue(++rcvevents);
410   if(rcvevents >= numevents) accept();
411 }