Finished logical separation of core and GUI. Monolithic build should work as expected.
[quassel.git] / gui / serverlist.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005 by The Quassel 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) 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 "serverlist.h"
22 #include "quassel.h"
23
24 /* NOTE: This dialog holds not only the server list, but also the identities.
25  *       This makes perfect sense given the fact that connections are initiated from
26  *       this dialog, and that the dialog exists during the lifetime of the program.
27  *       This data is also only used from within the GUI, which means it shouldn't be
28  *       part of the global Quassel class (me thinks).
29  */
30
31 ServerListDlg::ServerListDlg(QWidget *parent) : QDialog(parent) {
32   ui.setupUi(this);
33
34   QSettings settings;
35   settings.beginGroup("Network");
36   ui.showOnStartup->setChecked(settings.value("ShowServerListOnStartup", true).toBool());
37   // create some default entries
38   VarMap s1, s2, s3, s4;
39
40   s1["group"] = "Private Servers";
41   networks["mindNet"] = s1;
42   s2["group"] = "Private Servers";
43   networks["fooBar"] = s2;
44   s3["group"] = "";
45   networks["Single"] = s3;
46   s4["group"] = "Public Servers";
47   networks["public"] = s4;
48
49   // load networks from QSettings here instead
50   // [...]
51
52   // Construct tree widget (and its items) from networks
53   QStringList headers;
54   headers << "Network" << "Autoconnect";
55   ui.networkTree->setHeaderLabels(headers);
56   QHash<QString, QTreeWidgetItem *> groups;
57   foreach(QString net, networks.keys()) {
58     VarMap s = networks[net].toMap();
59     QString gr = s["group"].toString();
60     QTreeWidgetItem *item = 0;
61     if(gr == "") {
62       item = new QTreeWidgetItem(ui.networkTree);
63     } else {
64       if(groups.contains(gr)) {
65         item = new QTreeWidgetItem(groups[gr]);
66       } else {
67         QTreeWidgetItem *newgr = new QTreeWidgetItem(ui.networkTree);
68         newgr->setText(0, gr);
69         newgr->setFlags(newgr->flags() & ~Qt::ItemIsSelectable);
70         ui.networkTree->expandItem(newgr);
71         groups[gr] = newgr;
72         item = new QTreeWidgetItem(newgr);
73       }
74     }
75     item->setText(0, net);
76     //item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
77     item->setCheckState(1, Qt::Unchecked);
78   }
79   connect(ui.networkTree, SIGNAL(itemSelectionChanged()), this, SLOT(updateButtons()));
80
81   loadIdentities();
82   settings.endGroup();
83 }
84
85 ServerListDlg::~ServerListDlg() {
86
87 }
88
89 void ServerListDlg::updateButtons() {
90   QList<QTreeWidgetItem *> selected = ui.networkTree->selectedItems();
91   ui.editButton->setEnabled(selected.size() == 1);
92   ui.deleteButton->setEnabled(selected.size() >= 1);
93   ui.connectButton->setEnabled(selected.size() >= 1);
94
95 }
96
97 bool ServerListDlg::showOnStartup() {
98   return ui.showOnStartup->isChecked();
99 }
100
101 void ServerListDlg::on_addButton_clicked() {
102   NetworkEditDlg dlg(this, VarMap(), identities);
103   if(dlg.exec() == QDialog::Accepted) {
104
105   }
106 }
107
108 void ServerListDlg::loadNetworks() {
109
110 }
111
112 void ServerListDlg::storeNetworks() {
113
114 }
115
116 void ServerListDlg::loadIdentities() {
117  //QSettings s;
118   //s.beginGroup("Identities");
119   //identities = s.value("Network/Identities").toMap();
120   //identities = GuiProxy::loadIdentities();
121   identities = quassel->getData("Identities").toMap();
122   while(!identities.contains("Default")) {
123     identities = VarMap();
124     editIdentities();
125   }
126 }
127
128 void ServerListDlg::storeIdentities() {
129   //QSettings s;
130   //s.setValue("Network/Identities", identities);
131   //GuiProxy::storeIdentities(identities);
132   quassel->putData("Identities", identities);
133 }
134
135 void ServerListDlg::editIdentities() {
136   IdentitiesDlg dlg(this, identities);
137   if(dlg.exec() == QDialog::Accepted) {
138     identities = dlg.getIdentities();
139     QMap<QString, QString> mapping = dlg.getNameMapping();
140     // add mapping here
141
142     //
143     storeIdentities();
144     storeNetworks();  // ? how to treat mapping and NOT save changes not yet applied to the server list?
145   }
146 }
147
148 void ServerListDlg::on_showOnStartup_stateChanged(int) {
149   QSettings s;
150   s.setValue("Network/ShowServerListOnStartup", ui.showOnStartup->isChecked());
151 }
152
153 /***************************************************************************/
154
155 NetworkEditDlg::NetworkEditDlg(QWidget *parent, VarMap _network, VarMap _identities) : QDialog(parent) {
156   ui.setupUi(this);
157   network = _network;
158   identities = _identities;
159
160   ui.identityList->addItem(tr("Default Identity"));
161
162   foreach(QString id, identities.keys()) {
163     if(id != "Default") ui.identityList->addItem(id);
164   }
165
166 }
167
168 VarMap NetworkEditDlg::createDefaultNetwork() {
169   VarMap net;
170
171   net["group"] = "";
172
173   return net;
174 }
175
176 /***************************************************************************/
177
178 IdentitiesDlg::IdentitiesDlg(QWidget *parent, VarMap _identities) : QDialog(parent) {
179   ui.setupUi(this);
180   connect(ui.enableAutoAway, SIGNAL(stateChanged(int)), this, SLOT(autoAwayChecked()));
181
182   identities = _identities;
183   foreach(QString name, identities.keys()) {
184     nameMapping[name] = name;
185   }
186   if(identities.size() == 0) {
187     VarMap id = createDefaultIdentity();
188     id["IdName"] = "Default";
189     identities["Default"] = id;
190   }
191   ui.identityList->addItem(tr("Default Identity"));
192
193   foreach(QString id, identities.keys()) {
194     if(id != "Default") ui.identityList->addItem(id);
195   }
196   updateWidgets();
197   lastIdentity = getCurIdentity();
198   connect(ui.identityList, SIGNAL(activated(QString)), this, SLOT(identityChanged(QString)));
199   connect(ui.editIdentitiesButton, SIGNAL(clicked()), this, SLOT(editIdentities()));
200   connect(ui.nickList, SIGNAL(itemSelectionChanged()), this, SLOT(nickSelectionChanged()));
201   connect(ui.addNickButton, SIGNAL(clicked()), this, SLOT(addNick()));
202   connect(ui.editNickButton, SIGNAL(clicked()), this, SLOT(editNick()));
203   connect(ui.delNickButton, SIGNAL(clicked()), this, SLOT(delNick()));
204   connect(ui.upNickButton, SIGNAL(clicked()), this, SLOT(upNick()));
205   connect(ui.downNickButton, SIGNAL(clicked()), this, SLOT(downNick()));
206 }
207
208 VarMap IdentitiesDlg::createDefaultIdentity() {
209   VarMap id;
210   id["RealName"] = "foo";
211   id["Ident"] = "";
212   id["NickList"] = QStringList();
213   id["enableAwayNick"] = false;
214   id["AwayNick"] = "";
215   id["enableAwayReason"] = false;
216   id["AwayReason"] = "";
217   id["enableReturnMessage"] = false;
218   id["ReturnMessage"] = "";
219   id["enableAutoAway"] = false;
220   id["AutoAwayTime"] = 10;
221   id["enableAutoAwayReason"] = false;
222   id["AutoAwayReason"] = "";
223   id["enableAutoAwayReturn"] = false;
224   id["AutoAwayReturn"] = "";
225   id["PartReason"] = "Quasseling elsewhere.";
226   id["QuitReason"] = "Every Quassel comes to its end.";
227   id["KickReason"] = "No more quasseling for you!";
228
229   return id;
230 }
231
232 QString IdentitiesDlg::getCurIdentity() {
233   if(ui.identityList->currentIndex() == 0) return "Default";
234   return ui.identityList->currentText();
235 }
236
237 void IdentitiesDlg::updateWidgets() {
238   VarMap id = identities[getCurIdentity()].toMap();
239   ui.realNameEdit->setText(id["RealName"].toString());
240   ui.identEdit->setText(id["Ident"].toString());
241   ui.nickList->clear();
242   ui.nickList->addItems(id["NickList"].toStringList());
243   if(ui.nickList->count()>0) ui.nickList->setCurrentRow(0);
244   ui.enableAwayNick->setChecked(id["enableAwayNick"].toBool());
245   ui.awayNickEdit->setText(id["AwayNick"].toString());
246   ui.awayNickEdit->setEnabled(ui.enableAwayNick->isChecked());
247   ui.enableAwayReason->setChecked(id["enableAwayReason"].toBool());
248   ui.awayReasonEdit->setText(id["AwayReason"].toString());
249   ui.awayReasonEdit->setEnabled(ui.enableAwayReason->isChecked());
250   ui.enableReturnMessage->setChecked(id["enableReturnMessage"].toBool());
251   ui.returnMessageEdit->setText(id["ReturnMessage"].toString());
252   ui.returnMessageEdit->setEnabled(ui.enableReturnMessage->isChecked());
253   ui.enableAutoAway->setChecked(id["enableAutoAway"].toBool());
254   ui.autoAwayTime->setValue(id["AutoAwayTime"].toInt());
255   ui.enableAutoAwayReason->setChecked(id["enableAutoAwayReason"].toBool());
256   ui.autoAwayReasonEdit->setText(id["AutoAwayReason"].toString());
257   ui.enableAutoAwayReturn->setChecked(id["enableAutoAwayReturn"].toBool());
258   ui.autoAwayReturnEdit->setText(id["AutoAwayReturn"].toString());
259   ui.partReasonEdit->setText(id["PartReason"].toString());
260   ui.kickReasonEdit->setText(id["KickReason"].toString());
261   ui.quitReasonEdit->setText(id["QuitReason"].toString());
262   // set enabled states correctly
263   autoAwayChecked();
264   nickSelectionChanged();
265 }
266
267 void IdentitiesDlg::updateIdentity(QString idName) {
268   VarMap id;
269   id["RealName"] = ui.realNameEdit->text();
270   id["Ident"] = ui.identEdit->text();
271   QStringList nicks;
272   for(int i = 0; i < ui.nickList->count(); i++) nicks << ui.nickList->item(i)->text();
273   id["NickList"] = nicks;
274   id["enableAwayNick"] = ui.enableAwayNick->isChecked();
275   id["AwayNick"] = ui.awayNickEdit->text();
276   id["enableAwayReason"] = ui.enableAwayReason->isChecked();
277   id["AwayReason"] = ui.awayReasonEdit->text();
278   id["enableReturnMessage"] = ui.enableReturnMessage->isChecked();
279   id["ReturnMessage"] = ui.returnMessageEdit->text();
280   id["enableAutoAway"] = ui.enableAutoAway->isChecked();
281   id["AutoAwayTime"] = ui.autoAwayTime->value();
282   id["enableAutoAwayReason"] = ui.enableAutoAwayReason->isChecked();
283   id["AutoAwayReason"] = ui.autoAwayReasonEdit->text();
284   id["enableAutoAwayReturn"] = ui.enableAutoAwayReturn->isChecked();
285   id["AutoAwayReturn"] = ui.autoAwayReturnEdit->text();
286   id["PartReason"] = ui.partReasonEdit->text();
287   id["KickReason"] = ui.kickReasonEdit->text();
288   id["QuitReason"] = ui.quitReasonEdit->text();
289
290   id["IdName"] = idName;
291   identities[idName] = id;
292 }
293
294 void IdentitiesDlg::identityChanged(QString) {
295   updateIdentity(lastIdentity);
296   lastIdentity = getCurIdentity();
297   updateWidgets();
298 }
299
300 void IdentitiesDlg::autoAwayChecked() {
301   if(ui.enableAutoAway->isChecked()) {
302     ui.autoAwayLabel_1->setEnabled(1);
303     ui.autoAwayLabel_2->setEnabled(1);
304     ui.autoAwayTime->setEnabled(1);
305     ui.enableAutoAwayReason->setEnabled(1);
306     ui.enableAutoAwayReturn->setEnabled(1);
307     ui.autoAwayReasonEdit->setEnabled(ui.enableAutoAwayReason->isChecked());
308     ui.autoAwayReturnEdit->setEnabled(ui.enableAutoAwayReturn->isChecked());
309   } else {
310     ui.autoAwayLabel_1->setEnabled(0);
311     ui.autoAwayLabel_2->setEnabled(0);
312     ui.autoAwayTime->setEnabled(0);
313     ui.enableAutoAwayReason->setEnabled(0);
314     ui.enableAutoAwayReturn->setEnabled(0);
315     ui.autoAwayReasonEdit->setEnabled(0);
316     ui.autoAwayReturnEdit->setEnabled(0);
317   }
318 }
319
320 void IdentitiesDlg::nickSelectionChanged() {
321   int curidx = ui.nickList->currentRow();
322   ui.editNickButton->setEnabled(curidx >= 0);
323   ui.delNickButton->setEnabled(curidx >= 0);
324   ui.upNickButton->setEnabled(curidx > 0);
325   ui.downNickButton->setEnabled(curidx >= 0 && curidx < ui.nickList->count() - 1);
326 }
327
328 void IdentitiesDlg::addNick() {
329   NickEditDlg dlg(this);
330   if(dlg.exec() == QDialog::Accepted) {
331     QListWidgetItem *item = new QListWidgetItem(ui.nickList);
332     item->setText(dlg.getNick());
333     item->setFlags(item->flags() | Qt::ItemIsEditable);
334     ui.nickList->setCurrentItem(item);
335     nickSelectionChanged();
336   }
337 }
338
339 void IdentitiesDlg::editNick() {
340   NickEditDlg dlg(this, ui.nickList->currentItem()->text());
341   if(dlg.exec() == QDialog::Accepted) {
342     ui.nickList->currentItem()->setText(dlg.getNick());
343   }
344 }
345
346 void IdentitiesDlg::delNick() {
347   int row = ui.nickList->currentRow();
348   delete ui.nickList->takeItem(row);
349   if(row <= ui.nickList->count() - 1) ui.nickList->setCurrentRow(row);
350   else if(row > 0) ui.nickList->setCurrentRow(ui.nickList->count()-1);
351   nickSelectionChanged();
352 }
353
354 void IdentitiesDlg::upNick() {
355   int row = ui.nickList->currentRow();
356   QListWidgetItem *item = ui.nickList->takeItem(row);
357   ui.nickList->insertItem(row-1, item);
358   ui.nickList->setCurrentRow(row-1);
359   nickSelectionChanged();
360 }
361
362 void IdentitiesDlg::downNick() {
363   int row = ui.nickList->currentRow();
364   QListWidgetItem *item = ui.nickList->takeItem(row);
365   ui.nickList->insertItem(row+1, item);
366   ui.nickList->setCurrentRow(row+1);
367   nickSelectionChanged();
368 }
369
370 void IdentitiesDlg::accept() {
371   updateIdentity(getCurIdentity());
372   QString result = checkValidity();
373   if(result.length() == 0) QDialog::accept();
374   else {
375     QMessageBox::warning(this, tr("Invalid Identity!"),
376                          tr("One or more of your identities do not contain all necessary information:\n\n%1\n"
377                              "Please fill in any missing information.").arg(result));
378   }
379 }
380
381 QString IdentitiesDlg::checkValidity() {
382   QString reason;
383   foreach(QString name, identities.keys()) {
384     QString r;
385     VarMap id = identities[name].toMap();
386     if(name == "Default") name = tr("Default Identity");
387     if(id["RealName"].toString().length() == 0) {
388       r += tr(" You have not set a real name.");
389     }
390     if(id["Ident"].toString().length() == 0) {
391       r += tr(" You have to specify an Ident.");
392     }
393     if(id["NickList"].toStringList().size() == 0) {
394       r += tr(" You haven't entered any nicknames.");
395     }
396     if(r.length()>0) {
397       reason += tr("[%1]%2\n").arg(name).arg(r);
398     }
399   }
400   return reason;
401 }
402
403 void IdentitiesDlg::editIdentities() {
404   updateIdentity(getCurIdentity());
405   IdentitiesEditDlg dlg(this, identities, nameMapping, createDefaultIdentity());
406   if(dlg.exec() == QDialog::Accepted) {
407     identities = dlg.getIdentities();
408     nameMapping = dlg.getMapping();
409     ui.identityList->clear();
410     ui.identityList->addItem(tr("Default Identity"));
411     foreach(QString id, identities.keys()) {
412       if(id != "Default") ui.identityList->addItem(id);
413     }
414     lastIdentity = getCurIdentity();
415     updateWidgets();
416   }
417 }
418
419 /******************************************************************************/
420
421 IdentitiesEditDlg::IdentitiesEditDlg(QWidget *parent, VarMap _identities, QMap<QString, QString> _mapping, VarMap templ)
422   : QDialog(parent) {
423   ui.setupUi(this);
424   identities = _identities;
425   mapping = _mapping;
426   identTemplate = templ;
427
428   foreach(QString name, identities.keys()) {
429     if(name == "Default") continue;
430     ui.identList->addItem(name);
431   }
432   ui.identList->sortItems();
433   ui.identList->insertItem(0, tr("Default Identity"));
434   ui.identList->setCurrentRow(0);
435   selectionChanged();
436   connect(ui.identList, SIGNAL(itemSelectionChanged()), this, SLOT(selectionChanged()));
437   connect(ui.addButton, SIGNAL(clicked()), this, SLOT(addIdentity()));
438   connect(ui.duplicateButton, SIGNAL(clicked()), this, SLOT(duplicateIdentity()));
439   connect(ui.renameButton, SIGNAL(clicked()), this, SLOT(renameIdentity()));
440   connect(ui.deleteButton, SIGNAL(clicked()), this, SLOT(deleteIdentity()));
441 }
442
443 void IdentitiesEditDlg::selectionChanged() {
444   int idx = ui.identList->currentRow();
445   ui.duplicateButton->setEnabled(idx >= 0);
446   ui.renameButton->setEnabled(idx > 0);
447   ui.deleteButton->setEnabled(idx > 0);
448
449 }
450
451 void IdentitiesEditDlg::addIdentity() {
452   RenameIdentityDlg dlg(this, identities.keys());
453   if(dlg.exec() == QDialog::Accepted) {
454     VarMap id = identTemplate;
455     identities[dlg.getName()] = id;
456     QListWidgetItem *item = new QListWidgetItem(dlg.getName(), ui.identList);
457     sortList();
458     ui.identList->setCurrentItem(item);
459     selectionChanged();
460   }
461 }
462
463 void IdentitiesEditDlg::duplicateIdentity() {
464   RenameIdentityDlg dlg(this, identities.keys());
465   if(dlg.exec() == QDialog::Accepted) {
466     QString curname = ui.identList->currentRow() == 0 ? "Default" : ui.identList->currentItem()->text();
467     QVariant id = identities[curname];
468     identities[dlg.getName()] = id;
469     QListWidgetItem *item = new QListWidgetItem(dlg.getName(), ui.identList);
470     sortList();
471     ui.identList->setCurrentItem(item);
472     selectionChanged();
473   }
474 }
475
476 void IdentitiesEditDlg::renameIdentity() {
477   QList<QString> names;
478   QString curname = ui.identList->currentItem()->text();
479   foreach(QString n, identities.keys()) {
480     if(n != curname) names.append(n);
481   }
482   RenameIdentityDlg dlg(this, names, curname);
483   if(dlg.exec() == QDialog::Accepted) {
484     QString newname = dlg.getName();
485     foreach(QString key, mapping.keys()) {
486       if(mapping[key] == curname) {
487         mapping[key] = newname;
488         break;
489       }
490     }
491     QVariant id = identities.take(curname);
492     identities[newname] = id;
493     QListWidgetItem *item = ui.identList->currentItem();
494     item->setText(newname);
495     sortList();
496     ui.identList->setCurrentItem(item);
497     selectionChanged();
498   }
499 }
500
501 void IdentitiesEditDlg::deleteIdentity() {
502   QString curname = ui.identList->currentItem()->text();
503   if(QMessageBox::question(this, tr("Delete Identity?"),
504      tr("Do you really want to delete identity \"%1\"?\nNetworks using this identity "
505         "will be reset to use the default identity.").arg(curname),
506     tr("&Delete"), tr("&Cancel"), QString(), 1, 1) == 0) {
507       delete ui.identList->takeItem(ui.identList->currentRow());
508       foreach(QString key, mapping.keys()) {
509         if(mapping[key] == curname) {
510           mapping.remove(key); break;
511         }
512       }
513       identities.remove(curname);
514       selectionChanged();
515   }
516 }
517
518 void IdentitiesEditDlg::sortList() {
519   QListWidgetItem *def = ui.identList->takeItem(0);
520   ui.identList->sortItems();
521   ui.identList->insertItem(0, def);
522 }
523
524 /******************************************************************************/
525
526 NickEditDlg::NickEditDlg(QWidget *parent, QString nick) : QDialog(parent) {
527   ui.setupUi(this);
528   ui.lineEdit->setText(nick);
529   connect(ui.lineEdit, SIGNAL(textChanged(QString)), this, SLOT(textChanged(QString)));
530   textChanged(nick);
531 }
532
533 void NickEditDlg::textChanged(QString text) {
534   ui.okButton->setDisabled(text.isEmpty() || text == "");
535 }
536
537 QString NickEditDlg::getNick() {
538   return ui.lineEdit->text();
539 }
540
541 /*******************************************************************************/
542
543 RenameIdentityDlg::RenameIdentityDlg(QWidget *parent, QList<QString> _reserved, QString name) : QDialog(parent) {
544   ui.setupUi(this);
545   reserved = _reserved;
546   //ui.NickEditDlg->setWindowTitle(tr("Edit Identity Name"));  // why does this not work?
547   ui.label->setText(tr("Identity:"));
548   ui.lineEdit->setText(name);
549   connect(ui.lineEdit, SIGNAL(textChanged(QString)), this, SLOT(textChanged(QString)));
550   textChanged(name);
551 }
552
553 void RenameIdentityDlg::textChanged(QString text) {
554   if(text.length() == 0) { ui.okButton->setEnabled(0); return; }
555   ui.okButton->setDisabled(reserved.contains(text));
556 }
557
558 QString RenameIdentityDlg::getName() {
559   return ui.lineEdit->text();
560 }
561