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