It's the small things.. IgnoreListEditDlg++
[quassel.git] / src / qtui / settingspages / ignorelistsettingspage.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 "ignorelistsettingspage.h"
22
23 #include <QHeaderView>
24 #include <QItemSelectionModel>
25 #include <QModelIndex>
26 #include <QPainter>
27 #include <QMessageBox>
28 #include <QString>
29 #include <QEvent>
30 #include <QDebug>
31 #include "iconloader.h"
32
33 IgnoreListSettingsPage::IgnoreListSettingsPage(QWidget *parent)
34   : SettingsPage(tr("Misc"), tr("Ignore List"), parent)
35 {
36   ui.setupUi(this);
37   _delegate = new IgnoreListDelegate(ui.ignoreListView);
38   ui.newIgnoreRuleButton->setIcon(SmallIcon("list-add"));
39   ui.deleteIgnoreRuleButton->setIcon(SmallIcon("edit-delete"));
40   ui.editIgnoreRuleButton->setIcon(SmallIcon("configure"));
41
42   ui.ignoreListView->setSelectionBehavior(QAbstractItemView::SelectRows);
43   ui.ignoreListView->setSelectionMode(QAbstractItemView::SingleSelection);
44   ui.ignoreListView->setAlternatingRowColors(true);
45   ui.ignoreListView->setTabKeyNavigation(false);
46   ui.ignoreListView->setModel(&_ignoreListModel);
47  // ui.ignoreListView->setEditTriggers(QAbstractItemView::NoEditTriggers);
48
49   // ui.ignoreListView->setSortingEnabled(true);
50   ui.ignoreListView->verticalHeader()->hide();
51   ui.ignoreListView->hideColumn(1);
52   ui.ignoreListView->resizeColumnToContents(0);
53   ui.ignoreListView->horizontalHeader()->setStretchLastSection(true);
54   ui.ignoreListView->setItemDelegateForColumn(0, _delegate);
55   ui.ignoreListView->viewport()->setAttribute(Qt::WA_Hover);
56   ui.ignoreListView->viewport()->setMouseTracking(true);
57
58   connect(ui.ignoreListView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, SLOT(selectionChanged(const QItemSelection &, const QItemSelection &)));
59   connect(ui.newIgnoreRuleButton, SIGNAL(clicked()), this, SLOT(newIgnoreRule()));
60   connect(ui.deleteIgnoreRuleButton, SIGNAL(clicked()), this, SLOT(deleteSelectedIgnoreRule()));
61   connect(ui.editIgnoreRuleButton, SIGNAL(clicked()), this, SLOT(editSelectedIgnoreRule()));
62   connect(&_ignoreListModel, SIGNAL(configChanged(bool)), this, SLOT(setChangedState(bool)));
63   connect(&_ignoreListModel, SIGNAL(modelReady(bool)), this, SLOT(enableDialog(bool)));
64
65   enableDialog(_ignoreListModel.isReady());
66 }
67
68 IgnoreListSettingsPage::~IgnoreListSettingsPage() {
69   delete _delegate;
70 }
71
72 void IgnoreListSettingsPage::load() {
73   if(_ignoreListModel.configChanged())
74     _ignoreListModel.revert();
75   ui.ignoreListView->selectionModel()->reset();
76   ui.editIgnoreRuleButton->setEnabled(false);
77 }
78
79 void IgnoreListSettingsPage::defaults() {
80   _ignoreListModel.loadDefaults();
81 }
82
83 void IgnoreListSettingsPage::save() {
84   if(_ignoreListModel.configChanged()) {
85     _ignoreListModel.commit();
86   }
87   ui.ignoreListView->selectionModel()->reset();
88   ui.editIgnoreRuleButton->setEnabled(false);
89 }
90
91 void IgnoreListSettingsPage::enableDialog(bool enabled) {
92   ui.newIgnoreRuleButton->setEnabled(enabled);
93   setEnabled(enabled);
94 }
95
96 void IgnoreListSettingsPage::selectionChanged(const QItemSelection &selection, const QItemSelection &) {
97   bool state = !selection.isEmpty();
98   ui.deleteIgnoreRuleButton->setEnabled(state);
99   ui.editIgnoreRuleButton->setEnabled(state);
100 }
101
102 void IgnoreListSettingsPage::deleteSelectedIgnoreRule() {
103   if(!ui.ignoreListView->selectionModel()->hasSelection())
104     return;
105
106   _ignoreListModel.removeIgnoreRule(ui.ignoreListView->selectionModel()->selectedIndexes()[0].row());
107 }
108
109 void IgnoreListSettingsPage::newIgnoreRule(QString rule) {
110   IgnoreListManager::IgnoreListItem newItem = IgnoreListManager::IgnoreListItem();
111   newItem.strictness = IgnoreListManager::SoftStrictness;
112   newItem.scope = IgnoreListManager::GlobalScope;
113   newItem.isRegEx = false;
114   newItem.isActive = true;
115
116   bool enableOkButton = false;
117   if(!rule.isEmpty()) {
118     // we're called from contextmenu
119     newItem.ignoreRule = rule;
120     enableOkButton = true;
121   }
122
123   IgnoreListEditDlg *dlg = new IgnoreListEditDlg(newItem, this, enableOkButton);
124   dlg->enableOkButton(enableOkButton);
125   while(dlg->exec() == QDialog::Accepted) {
126     if(!_ignoreListModel.newIgnoreRule(dlg->ignoreListItem())) {
127       if(QMessageBox::warning(this,
128                          tr("Rule already exists"),
129                          tr("There is already a rule\n\"%1\"\nPlease choose another rule.")
130                          .arg(dlg->ignoreListItem().ignoreRule),
131                          QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok)
132           == QMessageBox::Cancel)
133         break;
134
135       IgnoreListManager::IgnoreListItem item = dlg->ignoreListItem();
136       delete dlg;
137       dlg = new IgnoreListEditDlg(item, this);
138     }
139     else {
140       break;
141     }
142   }
143   dlg->deleteLater();
144 }
145
146 void IgnoreListSettingsPage::editSelectedIgnoreRule() {
147   if(!ui.ignoreListView->selectionModel()->hasSelection())
148     return;
149   int row = ui.ignoreListView->selectionModel()->selectedIndexes()[0].row();
150   IgnoreListEditDlg dlg(_ignoreListModel.ignoreListItemAt(row), this);
151   dlg.setAttribute(Qt::WA_DeleteOnClose, false);
152   if(dlg.exec() == QDialog::Accepted) {
153     _ignoreListModel.setIgnoreListItemAt(row, dlg.ignoreListItem());
154   }
155 }
156
157 void IgnoreListSettingsPage::editIgnoreRule(const QString &ignoreRule) {
158   ui.ignoreListView->selectionModel()->select(_ignoreListModel.indexOf(ignoreRule), QItemSelectionModel::Select);
159   if(ui.ignoreListView->selectionModel()->hasSelection())// && ui.ignoreListView->selectionModel()->selectedIndexes()[0].row() != -1)
160     editSelectedIgnoreRule();
161   else
162     newIgnoreRule(ignoreRule);
163 }
164
165 /*
166   IgnoreListDelegate
167 */
168 void IgnoreListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
169   if(index.column() == 0) {
170     QStyle *style = QApplication::style();
171     if(option.state & QStyle::State_Selected)
172       painter->fillRect(option.rect, option.palette.highlight());
173
174     QStyleOptionButton opts;
175     opts.direction = option.direction;
176     opts.rect = option.rect;
177     opts.rect.moveLeft(option.rect.center().rx()-10);
178     opts.state = option.state;
179     opts.state |= index.data().toBool() ? QStyle::State_On : QStyle::State_Off;
180     style->drawControl(QStyle::CE_CheckBox, &opts, painter);
181   }
182   else
183     QStyledItemDelegate::paint(painter, option, index);
184 }
185
186 // provide interactivity for the checkboxes
187 bool IgnoreListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
188                                      const QStyleOptionViewItem &option, const QModelIndex &index) {
189   Q_UNUSED(option)
190   switch(event->type()) {
191     case QEvent::MouseButtonRelease:
192       model->setData(index, !index.data().toBool());
193       return true;
194       // don't show the default editor for the column
195     case QEvent::MouseButtonDblClick:
196       return true;
197     default:
198       return false;
199   }
200 }
201
202 /*
203   IgnoreListEditDlg
204 */
205 IgnoreListEditDlg::IgnoreListEditDlg(const IgnoreListManager::IgnoreListItem &item, QWidget *parent, bool enabled)
206     : QDialog(parent), _ignoreListItem(item), _hasChanged(enabled) {
207   ui.setupUi(this);
208   setAttribute(Qt::WA_DeleteOnClose, false);
209   setModal(true);
210   // FIXME patch out the bugger completely if it's good without it
211   ui.isActiveCheckBox->hide();
212
213   // setup buttongroups
214   // this could be moved to .ui file with qt4.5
215   _typeButtonGroup.addButton(ui.senderTypeButton, 0);
216   _typeButtonGroup.addButton(ui.messageTypeButton, 1);
217   _strictnessButtonGroup.addButton(ui.dynamicStrictnessButton, 0);
218   _strictnessButtonGroup.addButton(ui.permanentStrictnessButton, 1);
219   _scopeButtonGroup.addButton(ui.globalScopeButton, 0);
220   _scopeButtonGroup.addButton(ui.networkScopeButton, 1);
221   _scopeButtonGroup.addButton(ui.channelScopeButton, 2);
222
223   ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
224
225   ui.ignoreRuleLineEdit->setText(item.ignoreRule);
226
227   if(item.type == IgnoreListManager::MessageIgnore)
228     ui.messageTypeButton->setChecked(true);
229   else
230     ui.senderTypeButton->setChecked(true);
231
232   ui.isRegExCheckBox->setChecked(item.isRegEx);
233   ui.isActiveCheckBox->setChecked(item.isActive);
234
235   if(item.strictness == IgnoreListManager::HardStrictness)
236     ui.permanentStrictnessButton->setChecked(true);
237   else
238     ui.dynamicStrictnessButton->setChecked(true);
239
240   switch(item.scope) {
241     case IgnoreListManager::NetworkScope:
242       ui.networkScopeButton->setChecked(true);
243       ui.scopeRuleTextEdit->setEnabled(true);
244       break;
245     case IgnoreListManager::ChannelScope:
246       ui.channelScopeButton->setChecked(true);
247       ui.scopeRuleTextEdit->setEnabled(true);
248       break;
249     default:
250       ui.globalScopeButton->setChecked(true);
251       ui.scopeRuleTextEdit->setEnabled(false);
252   }
253
254   if(item.scope == IgnoreListManager::GlobalScope)
255     ui.scopeRuleTextEdit->clear();
256   else
257     ui.scopeRuleTextEdit->setPlainText(item.scopeRule);
258
259   connect(ui.ignoreRuleLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(widgetHasChanged()));
260   connect(ui.scopeRuleTextEdit, SIGNAL(textChanged()), this, SLOT(widgetHasChanged()));
261   connect(&_typeButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(widgetHasChanged()));
262   connect(&_strictnessButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(widgetHasChanged()));
263   connect(&_scopeButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(widgetHasChanged()));
264   connect(ui.isRegExCheckBox, SIGNAL(stateChanged(int)), this, SLOT(widgetHasChanged()));
265   connect(ui.isActiveCheckBox, SIGNAL(stateChanged(int)), this, SLOT(widgetHasChanged()));
266
267   connect(ui.buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(aboutToAccept()));
268   widgetHasChanged();
269 }
270
271 void IgnoreListEditDlg::widgetHasChanged() {
272   if(ui.messageTypeButton->isChecked())
273     _clonedIgnoreListItem.type = IgnoreListManager::MessageIgnore;
274   else
275     _clonedIgnoreListItem.type = IgnoreListManager::SenderIgnore;
276
277   if(ui.permanentStrictnessButton->isChecked())
278     _clonedIgnoreListItem.strictness = IgnoreListManager::HardStrictness;
279   else
280     _clonedIgnoreListItem.strictness = IgnoreListManager::SoftStrictness;
281
282   if(ui.networkScopeButton->isChecked()) {
283     _clonedIgnoreListItem.scope = IgnoreListManager::NetworkScope;
284     ui.scopeRuleTextEdit->setEnabled(true);
285   }
286   else if(ui.channelScopeButton->isChecked()) {
287     _clonedIgnoreListItem.scope = IgnoreListManager::ChannelScope;
288     ui.scopeRuleTextEdit->setEnabled(true);
289   }
290   else {
291     _clonedIgnoreListItem.scope = IgnoreListManager::GlobalScope;
292     ui.scopeRuleTextEdit->setEnabled(false);
293   }
294
295   if(_clonedIgnoreListItem.scope == IgnoreListManager::GlobalScope) {
296     _clonedIgnoreListItem.scopeRule = QString();
297   }
298   else {
299     QStringList text = ui.scopeRuleTextEdit->toPlainText().split(";", QString::SkipEmptyParts);
300     QStringList::iterator it = text.begin();
301     while(it != text.end())
302       (*it++).trimmed();
303
304     _clonedIgnoreListItem.scopeRule = text.join("; ");
305   }
306
307   _clonedIgnoreListItem.ignoreRule = ui.ignoreRuleLineEdit->text();
308   _clonedIgnoreListItem.isRegEx = ui.isRegExCheckBox->isChecked();
309   _clonedIgnoreListItem.isActive = ui.isActiveCheckBox->isChecked();
310
311   if(!_clonedIgnoreListItem.ignoreRule.isEmpty() && _clonedIgnoreListItem != _ignoreListItem)
312     _hasChanged = true;
313   else
314     _hasChanged = false;
315   ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(_hasChanged);
316 }
317
318 void IgnoreListEditDlg::enableOkButton(bool state) {
319   ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(state);
320 }