Refactorize settingspages a bit
[quassel.git] / src / qtui / settingspages / bufferviewsettingspage.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-09 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  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20
21 #include "bufferviewsettingspage.h"
22
23 #include <QMessageBox>
24
25 #include "client.h"
26 #include "iconloader.h"
27 #include "network.h"
28 #include "bufferviewconfig.h"
29 #include "bufferviewfilter.h"
30 #include "buffermodel.h"
31 #include "clientbufferviewmanager.h"
32 #include "networkmodel.h"
33 #include "util.h"
34
35 BufferViewSettingsPage::BufferViewSettingsPage(QWidget *parent)
36   : SettingsPage(tr("Interface"), tr("Custom Chat Lists"), parent),
37     _ignoreWidgetChanges(false),
38     _useBufferViewHint(false),
39     _bufferViewHint(0)
40 {
41   ui.setupUi(this);
42   ui.renameBufferView->setIcon(SmallIcon("edit-rename"));
43   ui.addBufferView->setIcon(SmallIcon("list-add"));
44   ui.deleteBufferView->setIcon(SmallIcon("edit-delete"));
45
46   reset();
47
48   ui.bufferViewList->setSortingEnabled(true);
49   ui.settingsGroupBox->setEnabled(false);
50   ui.bufferViewPreview->setEnabled(false);
51
52   coreConnectionStateChanged(Client::isConnected());  // need a core connection!
53   connect(Client::instance(), SIGNAL(coreConnectionStateChanged(bool)), this, SLOT(coreConnectionStateChanged(bool)));
54   connect(ui.bufferViewList->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
55           this, SLOT(bufferViewSelectionChanged(const QItemSelection &, const QItemSelection &)));
56
57   connect(ui.onlyStatusBuffers, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
58   connect(ui.onlyChannelBuffers, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
59   connect(ui.onlyQueryBuffers, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
60   connect(ui.addNewBuffersAutomatically, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
61   connect(ui.sortAlphabetically, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
62   connect(ui.hideInactiveBuffers, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
63   connect(ui.networkSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(widgetHasChanged()));
64   connect(ui.minimumActivitySelector, SIGNAL(currentIndexChanged(int)), this, SLOT(widgetHasChanged()));
65
66   connect(ui.networkSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(enableStatusBuffers(int)));
67 }
68
69 BufferViewSettingsPage::~BufferViewSettingsPage() {
70   reset();
71 }
72
73 void BufferViewSettingsPage::reset() {
74   ui.bufferViewList->clear();
75   ui.deleteBufferView->setEnabled(false);
76
77   QHash<BufferViewConfig *, BufferViewConfig *>::iterator changedConfigIter = _changedBufferViews.begin();
78   QHash<BufferViewConfig *, BufferViewConfig *>::iterator changedConfigIterEnd = _changedBufferViews.end();
79   BufferViewConfig *config;
80   while(changedConfigIter != changedConfigIterEnd) {
81     config = changedConfigIter.value();
82     changedConfigIter = _changedBufferViews.erase(changedConfigIter);
83     config->deleteLater();
84   }
85
86   QList<BufferViewConfig *>::iterator newConfigIter = _newBufferViews.begin();
87   QList<BufferViewConfig *>::iterator newConfigIterEnd = _newBufferViews.end();
88   while(newConfigIter != newConfigIterEnd) {
89     config = *newConfigIter;
90     newConfigIter = _newBufferViews.erase(newConfigIter);
91     config->deleteLater();
92   }
93
94   _deleteBufferViews.clear();
95
96   _useBufferViewHint = false;
97
98   setChangedState(false);
99 }
100
101 void BufferViewSettingsPage::load() {
102   bool useBufferViewHint = _useBufferViewHint;
103   int bufferViewHint = _bufferViewHint;
104   reset();
105
106   if(!Client::bufferViewManager())
107     return;
108
109   const QList<BufferViewConfig *> bufferViewConfigs = Client::bufferViewManager()->bufferViewConfigs();
110   foreach(BufferViewConfig *bufferViewConfig, bufferViewConfigs) {
111     addBufferView(bufferViewConfig);
112   }
113
114   _ignoreWidgetChanges = true;
115   // load network selector
116   ui.networkSelector->clear();
117   ui.networkSelector->addItem(tr("All"));
118   ui.networkSelector->setItemData(0, qVariantFromValue<NetworkId>(NetworkId()));
119   const Network *net;
120   foreach(NetworkId netId, Client::networkIds()) {
121     net = Client::network(netId);
122     ui.networkSelector->addItem(net->networkName());
123     ui.networkSelector->setItemData(ui.networkSelector->count() - 1, qVariantFromValue<NetworkId>(net->networkId()));
124   }
125   _ignoreWidgetChanges = false;
126
127
128   if(!useBufferViewHint || !selectBufferViewById(bufferViewHint))
129     ui.bufferViewList->setCurrentRow(0);
130 }
131
132 void BufferViewSettingsPage::save() {
133   setEnabled(false);
134
135   BufferViewConfig *currentConfig = bufferView(ui.bufferViewList->currentRow());
136   if(currentConfig) {
137     _useBufferViewHint = true;
138     _bufferViewHint = currentConfig->bufferViewId();
139   }
140
141   QVariantList newConfigs;
142   QVariantList deleteConfigs;
143   QVariantList changedConfigs;
144
145   foreach(int bufferId, _deleteBufferViews) {
146     deleteConfigs << bufferId;
147   }
148   _deleteBufferViews.clear();
149   if(Client::bufferViewManager()) {
150     Client::bufferViewManager()->requestDeleteBufferViews(deleteConfigs);
151   }
152
153   QHash<BufferViewConfig *, BufferViewConfig *>::iterator changedConfigIter = _changedBufferViews.begin();
154   QHash<BufferViewConfig *, BufferViewConfig *>::iterator changedConfigIterEnd = _changedBufferViews.end();
155   BufferViewConfig *config, *changedConfig;
156   while(changedConfigIter != changedConfigIterEnd) {
157     config = changedConfigIter.key();
158     changedConfig = changedConfigIter.value();
159     changedConfigIter = _changedBufferViews.erase(changedConfigIter);
160     config->requestUpdate(changedConfig->toVariantMap());
161     changedConfig->deleteLater();
162   }
163
164   QList<BufferViewConfig *>::iterator newConfigIter = _newBufferViews.begin();
165   QList<BufferViewConfig *>::iterator newConfigIterEnd = _newBufferViews.end();
166   while(newConfigIter != newConfigIterEnd) {
167     config = *newConfigIter;
168     newConfigIter = _newBufferViews.erase(newConfigIter);
169     newConfigs << config->toVariantMap();
170     config->deleteLater();
171   }
172   if(Client::bufferViewManager()) {
173     Client::bufferViewManager()->requestCreateBufferViews(newConfigs);
174   }
175
176   load();
177   setEnabled(true);
178 }
179
180 void BufferViewSettingsPage::coreConnectionStateChanged(bool state) {
181   setEnabled(state);
182   if(state) {
183     load();
184     connect(Client::bufferViewManager(), SIGNAL(bufferViewConfigAdded(int)), this, SLOT(addBufferView(int)));
185   } else {
186     reset();
187   }
188 }
189
190 void BufferViewSettingsPage::addBufferView(BufferViewConfig *config) {
191   QListWidgetItem *item = new QListWidgetItem(config->bufferViewName(), ui.bufferViewList);
192   item->setData(Qt::UserRole, qVariantFromValue<QObject *>(qobject_cast<QObject *>(config)));
193   connect(config, SIGNAL(updatedRemotely()), this, SLOT(updateBufferView()));
194   connect(config, SIGNAL(destroyed()), this, SLOT(bufferViewDeleted()));
195   ui.deleteBufferView->setEnabled(ui.bufferViewList->count() > 1);
196 }
197
198 void BufferViewSettingsPage::addBufferView(int bufferViewId) {
199   // we are informed about a new bufferview from Client::bufferViewManager()
200   Q_ASSERT(Client::bufferViewManager());
201   addBufferView(Client::bufferViewManager()->bufferViewConfig(bufferViewId));
202   selectBufferViewById(bufferViewId);
203 }
204
205 void BufferViewSettingsPage::bufferViewDeleted() {
206   BufferViewConfig *config = static_cast<BufferViewConfig *>(sender());
207   QObject *obj;
208   for(int i = 0; i < ui.bufferViewList->count(); i++) {
209     obj = ui.bufferViewList->item(i)->data(Qt::UserRole).value<QObject *>();
210     if(config == static_cast<BufferViewConfig *>(obj)) {
211       QListWidgetItem *item = ui.bufferViewList->takeItem(i);
212       delete item;
213       break;
214     }
215   }
216   ui.deleteBufferView->setEnabled(ui.bufferViewList->count() > 1);
217 }
218
219 void BufferViewSettingsPage::newBufferView(const QString &bufferViewName) {
220   // id's of newly created bufferviews are negative (-1, -2... -n)
221   int fakeId = -1 * (_newBufferViews.count() + 1);
222   BufferViewConfig *config = new BufferViewConfig(fakeId);
223   config->setBufferViewName(bufferViewName);
224   config->setInitialized();
225   QList<BufferId> bufferIds;
226   if(config->addNewBuffersAutomatically()) {
227     if(config->sortAlphabetically()) {
228       bufferIds = Client::networkModel()->allBufferIdsSorted();
229     } else {
230       bufferIds = Client::networkModel()->allBufferIds();
231       qSort(bufferIds);
232       config->setProperty("OriginalBufferList", toVariantList<BufferId>(bufferIds));
233     }
234   }
235   config->initSetBufferList(bufferIds);
236
237   _newBufferViews << config;
238   addBufferView(config);
239   ui.bufferViewList->setCurrentRow(listPos(config));
240 }
241
242 int BufferViewSettingsPage::listPos(BufferViewConfig *config) {
243   QObject *obj;
244   for(int i = 0; i < ui.bufferViewList->count(); i++) {
245     obj = ui.bufferViewList->item(i)->data(Qt::UserRole).value<QObject *>();
246     if(config == qobject_cast<BufferViewConfig *>(obj))
247       return i;
248   }
249   return -1;
250 }
251
252 BufferViewConfig *BufferViewSettingsPage::bufferView(int listPos) {
253   if(listPos < ui.bufferViewList->count() && listPos >= 0) {
254     QObject *obj = ui.bufferViewList->item(listPos)->data(Qt::UserRole).value<QObject *>();
255     return qobject_cast<BufferViewConfig *>(obj);
256   } else {
257     return 0;
258   }
259 }
260
261 bool BufferViewSettingsPage::selectBufferViewById(int bufferViewId) {
262   BufferViewConfig *config;
263   for(int i = 0; i < ui.bufferViewList->count(); i++) {
264     config = qobject_cast<BufferViewConfig *>(ui.bufferViewList->item(i)->data(Qt::UserRole).value<QObject *>());
265     if(config && config->bufferViewId() == bufferViewId) {
266       ui.bufferViewList->setCurrentRow(i);
267       return true;
268     }
269   }
270   return false;
271 }
272
273 void BufferViewSettingsPage::updateBufferView() {
274   BufferViewConfig *config = qobject_cast<BufferViewConfig *>(sender());
275   if(!config)
276     return;
277
278   int itemPos = listPos(config);
279   if(itemPos == -1) {
280     qWarning() << "BufferViewSettingsPage::updateBufferView(): view is unknown:" << config->bufferViewId();
281     return;
282   }
283   ui.bufferViewList->item(itemPos)->setText(config->bufferViewName());
284   if(itemPos == ui.bufferViewList->currentRow())
285     loadConfig(config);
286 }
287
288 void BufferViewSettingsPage::enableStatusBuffers(int networkIdx) {
289   // we don't show a status buffer if we show multiple networks as selecting
290   // the network is the same as selecting the status buffer.
291   ui.onlyStatusBuffers->setEnabled(networkIdx != 0);
292 }
293
294 void BufferViewSettingsPage::on_addBufferView_clicked() {
295   if(!Client::bufferViewManager())
296     return;
297
298   QStringList existing;
299   foreach(BufferViewConfig *bufferConfig, Client::bufferViewManager()->bufferViewConfigs()) {
300     existing << bufferConfig->bufferViewName();
301   }
302
303   BufferViewEditDlg dlg(QString(), existing, this);
304   if(dlg.exec() == QDialog::Accepted) {
305     newBufferView(dlg.bufferViewName());
306     changed();
307   }
308 }
309
310 void BufferViewSettingsPage::on_renameBufferView_clicked() {
311   if(ui.bufferViewList->selectedItems().isEmpty())
312     return;
313
314   if(!Client::bufferViewManager())
315     return;
316
317   BufferViewConfig *config = bufferView(ui.bufferViewList->currentRow());
318   if(!config)
319     return;
320
321   QStringList existing;
322   foreach(BufferViewConfig *bufferConfig, Client::bufferViewManager()->bufferViewConfigs()) {
323     existing << bufferConfig->bufferViewName();
324   }
325
326   BufferViewEditDlg dlg(config->bufferViewName(), existing, this);
327   if(dlg.exec() == QDialog::Accepted) {
328     BufferViewConfig *changedConfig = cloneConfig(config);
329     changedConfig->setBufferViewName(dlg.bufferViewName());
330     ui.bufferViewList->item(listPos(config))->setText(dlg.bufferViewName());
331     changed();
332   }
333 }
334
335 void BufferViewSettingsPage::on_deleteBufferView_clicked() {
336   if(ui.bufferViewList->selectedItems().isEmpty())
337     return;
338
339   QListWidgetItem *currentItem = ui.bufferViewList->item(ui.bufferViewList->currentRow());
340   QString viewName = currentItem->text();
341   int viewId = bufferView(ui.bufferViewList->currentRow())->bufferViewId();
342   int ret = QMessageBox::question(this, tr("Delete Chat List?"),
343                                     tr("Do you really want to delete the chat list \"%1\"?").arg(viewName),
344                                     QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
345
346   if(ret == QMessageBox::Yes) {
347     ui.bufferViewList->removeItemWidget(currentItem);
348     BufferViewConfig *config = qobject_cast<BufferViewConfig *>(currentItem->data(Qt::UserRole).value<QObject *>());
349     delete currentItem;
350     if(viewId >= 0) {
351       _deleteBufferViews << viewId;
352       changed();
353     } else if(config) {
354       QList<BufferViewConfig *>::iterator iter = _newBufferViews.begin();
355       while(iter != _newBufferViews.end()) {
356         if(*iter == config) {
357           iter = _newBufferViews.erase(iter);
358           break;
359         } else {
360           iter++;
361         }
362       }
363       delete config;
364       if(_deleteBufferViews.isEmpty() && _changedBufferViews.isEmpty() && _newBufferViews.isEmpty())
365         setChangedState(false);
366     }
367   }
368 }
369
370 void BufferViewSettingsPage::bufferViewSelectionChanged(const QItemSelection &current, const QItemSelection &previous) {
371   Q_UNUSED(previous)
372
373   if(!current.isEmpty()) {
374     ui.settingsGroupBox->setEnabled(true);
375     ui.bufferViewPreview->setEnabled(true);
376
377     loadConfig(configForDisplay(bufferView(ui.bufferViewList->currentRow())));
378   } else {
379     ui.settingsGroupBox->setEnabled(false);
380     ui.bufferViewPreview->setEnabled(false);
381   }
382 }
383
384 void BufferViewSettingsPage::loadConfig(BufferViewConfig *config) {
385   if(!config)
386     return;
387
388   _ignoreWidgetChanges = true;
389   ui.onlyStatusBuffers->setChecked(BufferInfo::StatusBuffer & config->allowedBufferTypes());
390   ui.onlyChannelBuffers->setChecked(BufferInfo::ChannelBuffer & config->allowedBufferTypes());
391   ui.onlyQueryBuffers->setChecked(BufferInfo::QueryBuffer & config->allowedBufferTypes());
392   ui.addNewBuffersAutomatically->setChecked(config->addNewBuffersAutomatically());
393   ui.sortAlphabetically->setChecked(config->sortAlphabetically());
394   ui.hideInactiveBuffers->setChecked(config->hideInactiveBuffers());
395
396   int networkIndex = 0;
397   for(int i = 0; i < ui.networkSelector->count(); i++) {
398     if(ui.networkSelector->itemData(i).value<NetworkId>() == config->networkId()) {
399       networkIndex = i;
400       break;
401     }
402   }
403   ui.networkSelector->setCurrentIndex(networkIndex);
404
405   int activityIndex = 0;
406   int minimumActivity = config->minimumActivity();
407   while(minimumActivity) {
408     activityIndex++;
409     minimumActivity = minimumActivity >> 1;
410   }
411   ui.minimumActivitySelector->setCurrentIndex(activityIndex);
412
413   ui.bufferViewPreview->setFilteredModel(Client::bufferModel(), config);
414
415   _ignoreWidgetChanges = false;
416 }
417
418 void BufferViewSettingsPage::saveConfig(BufferViewConfig *config) {
419   if(!config)
420     return;
421
422   int allowedBufferTypes = 0;
423   if(ui.onlyStatusBuffers->isChecked())
424     allowedBufferTypes |= BufferInfo::StatusBuffer;
425   if(ui.onlyChannelBuffers->isChecked())
426     allowedBufferTypes |= BufferInfo::ChannelBuffer;
427   if(ui.onlyQueryBuffers->isChecked())
428     allowedBufferTypes |= BufferInfo::QueryBuffer;
429   config->setAllowedBufferTypes(allowedBufferTypes);
430
431   config->setAddNewBuffersAutomatically(ui.addNewBuffersAutomatically->isChecked());
432   config->setSortAlphabetically(ui.sortAlphabetically->isChecked());
433   config->setHideInactiveBuffers(ui.hideInactiveBuffers->isChecked());
434   config->setNetworkId(ui.networkSelector->itemData(ui.networkSelector->currentIndex()).value<NetworkId>());
435
436   int minimumActivity = 0;
437   if(ui.minimumActivitySelector->currentIndex() > 0)
438     minimumActivity = 1 << (ui.minimumActivitySelector->currentIndex() - 1);
439   config->setMinimumActivity(minimumActivity);
440
441   QList<BufferId> bufferIds = fromVariantList<BufferId>(config->property("OriginalBufferList").toList());
442   if(config->sortAlphabetically())
443     Client::networkModel()->sortBufferIds(bufferIds);
444
445   if(!_newBufferViews.contains(config) || config->addNewBuffersAutomatically())
446     config->initSetBufferList(bufferIds);
447 }
448
449 void BufferViewSettingsPage::widgetHasChanged() {
450   if(_ignoreWidgetChanges)
451     return;
452   setChangedState(testHasChanged());
453 }
454
455 bool BufferViewSettingsPage::testHasChanged() {
456   saveConfig(cloneConfig(bufferView(ui.bufferViewList->currentRow())));
457
458   if(!_newBufferViews.isEmpty())
459     return true;
460
461   bool changed = false;
462   QHash<BufferViewConfig *, BufferViewConfig *>::iterator iter = _changedBufferViews.begin();
463   QHash<BufferViewConfig *, BufferViewConfig *>::iterator iterEnd = _changedBufferViews.end();
464   while(iter != iterEnd) {
465     if(&(iter.key()) == &(iter.value())) {
466       iter.value()->deleteLater();
467       iter = _changedBufferViews.erase(iter);
468     } else {
469       changed = true;
470       iter++;
471     }
472   }
473   return changed;
474 }
475
476 BufferViewConfig *BufferViewSettingsPage::cloneConfig(BufferViewConfig *config) {
477   if(!config || config->bufferViewId() < 0)
478     return config;
479
480   if(_changedBufferViews.contains(config))
481     return _changedBufferViews[config];
482
483   BufferViewConfig *changedConfig = new BufferViewConfig(-1, this);
484   changedConfig->fromVariantMap(config->toVariantMap());
485   changedConfig->setInitialized();
486   _changedBufferViews[config] = changedConfig;
487   connect(config, SIGNAL(bufferAdded(const BufferId &, int)), changedConfig, SLOT(addBuffer(const BufferId &, int)));
488   connect(config, SIGNAL(bufferMoved(const BufferId &, int)), changedConfig, SLOT(moveBuffer(const BufferId &, int)));
489   connect(config, SIGNAL(bufferRemoved(const BufferId &)), changedConfig, SLOT(removeBuffer(const BufferId &)));
490 //   connect(config, SIGNAL(addBufferRequested(const BufferId &, int)), changedConfig, SLOT(addBuffer(const BufferId &, int)));
491 //   connect(config, SIGNAL(moveBufferRequested(const BufferId &, int)), changedConfig, SLOT(moveBuffer(const BufferId &, int)));
492 //   connect(config, SIGNAL(removeBufferRequested(const BufferId &)), changedConfig, SLOT(removeBuffer(const BufferId &)));
493
494   changedConfig->setProperty("OriginalBufferList", toVariantList<BufferId>(config->bufferList()));
495   // if this is the currently displayed view we have to change the config of the preview filter
496   BufferViewFilter *filter = qobject_cast<BufferViewFilter *>(ui.bufferViewPreview->model());
497   if(filter && filter->config() == config)
498     filter->setConfig(changedConfig);
499   ui.bufferViewPreview->setConfig(changedConfig);
500
501   return changedConfig;
502 }
503
504 BufferViewConfig *BufferViewSettingsPage::configForDisplay(BufferViewConfig *config) {
505   if(_changedBufferViews.contains(config))
506     return _changedBufferViews[config];
507   else
508     return config;
509 }
510
511
512 /**************************************************************************
513  * BufferViewEditDlg
514  *************************************************************************/
515 BufferViewEditDlg::BufferViewEditDlg(const QString &old, const QStringList &exist, QWidget *parent) : QDialog(parent), existing(exist) {
516   ui.setupUi(this);
517
518   if(old.isEmpty()) {
519     // new buffer
520     setWindowTitle(tr("Add Chat List"));
521     on_bufferViewEdit_textChanged(""); // disable ok button
522   } else {
523     ui.bufferViewEdit->setText(old);
524   }
525 }
526
527
528 void BufferViewEditDlg::on_bufferViewEdit_textChanged(const QString &text) {
529   ui.buttonBox->button(QDialogButtonBox::Ok)->setDisabled(text.isEmpty() || existing.contains(text));
530 }
531