1 /***************************************************************************
2 *                                                                         *
3 *   This program is free software; you can redistribute it and/or modify  *
4 *   it under the terms of the GNU General Public License as published by  *
5 *   the Free Software Foundation; either version 3 of the License, or     *
6 *   (at your option) any later version.                                   *
7 *                                                                         *
8 ***************************************************************************/
9 
10 #include "SearchBlacklistDialog.h"
11 #include "SearchBlacklist.h"
12 #include "WulforUtil.h"
13 
14 #include <QComboBox>
15 #include <QLineEdit>
16 #include <QMenu>
17 #include <QItemSelectionModel>
18 #include <QResizeEvent>
19 
SearchBlackListDialog(QWidget * parent)20 SearchBlackListDialog::SearchBlackListDialog(QWidget *parent): QDialog(parent){
21     setupUi(this);
22 
23     model = new SearchBlackListModel();
24     treeView_RULES->setModel(model);
25     treeView_RULES->setItemDelegate(new SearchBlackListDelegate(this));
26     treeView_RULES->setContextMenuPolicy(Qt::CustomContextMenu);
27     treeView_RULES->setSortingEnabled(true);
28     treeView_RULES->sortByColumn(COLUMN_SBL_KEY, Qt::AscendingOrder);
29 
30     connect(treeView_RULES, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotContextMenu()));
31     connect(this, SIGNAL(accepted()), this, SLOT(ok()));
32 }
33 
~SearchBlackListDialog()34 SearchBlackListDialog::~SearchBlackListDialog(){
35     model->deleteLater();
36 }
37 
ok()38 void SearchBlackListDialog::ok(){
39     model->save();
40 }
41 
resizeEvent(QResizeEvent * e)42 void SearchBlackListDialog::resizeEvent(QResizeEvent *e){
43     e->accept();
44 
45     treeView_RULES->resizeColumnToContents(COLUMN_SBL_TYPE);
46 
47     int sblTypeWidth = treeView_RULES->columnWidth(COLUMN_SBL_TYPE);
48     int sblKeyWidth = treeView_RULES->contentsRect().width() - sblTypeWidth;
49 
50     treeView_RULES->setColumnWidth(COLUMN_SBL_KEY, sblKeyWidth);
51 }
52 
slotContextMenu()53 void SearchBlackListDialog::slotContextMenu(){
54     QItemSelectionModel *s_m = treeView_RULES->selectionModel();
55     QModelIndexList indexes = s_m->selectedRows(0);
56 
57     QMenu *menu = new QMenu(this);
58     QAction *add = new QAction(WICON(WulforUtil::eiEDITADD), tr("Add new"), NULL);
59     QAction *rem = new QAction(WICON(WulforUtil::eiEDITDELETE), tr("Remove"), NULL);
60 
61     menu->addActions(QList<QAction*>() << add << rem);
62 
63     QAction *ret = menu->exec(QCursor::pos());
64 
65     menu->deleteLater();
66 
67     if (ret == add){
68         s_m->select(model->addEmptyItem(), QItemSelectionModel::SelectCurrent|QItemSelectionModel::Rows);
69     }
70     else if (ret && !indexes.isEmpty()){
71         for (const auto &index : indexes){
72             SearchBlackListItem *i = reinterpret_cast<SearchBlackListItem*>(index.internalPointer());
73 
74             if (!i)
75                 continue;
76 
77             i->parent()->childItems.removeAt(i->row());
78         }
79 
80         model->repaint();
81     }
82 }
83 
SearchBlackListModel(QObject * parent)84 SearchBlackListModel::SearchBlackListModel(QObject * parent) :
85         QAbstractItemModel(parent),
86         sortColumn(COLUMN_SBL_KEY)
87 {
88     rootItem = new SearchBlackListItem(NULL);
89 
90     SearchBlacklist *SB = SearchBlacklist::getInstance();
91 
92     QList<QString> names = SB->getList(SearchBlacklist::NAME);
93     QList<QString> tths  = SB->getList(SearchBlacklist::TTH);
94 
95     for (const auto &name : names){
96         SearchBlackListItem * item = new SearchBlackListItem(rootItem);
97         item->title = name;
98         item->argument = SearchBlacklist::NAME;
99 
100         rootItem->appendChild(item);
101     }
102 
103     for (const auto &tth : tths){
104         SearchBlackListItem * item = new SearchBlackListItem(rootItem);
105         item->title = tth;
106         item->argument = SearchBlacklist::TTH;
107 
108         rootItem->appendChild(item);
109     }
110 
111     sortColumn = -1;
112 }
113 
114 
~SearchBlackListModel()115 SearchBlackListModel::~SearchBlackListModel() {
116     delete rootItem;
117 }
118 
save()119 void SearchBlackListModel::save(){
120     QList<QString> names;
121     QList<QString> tths;
122 
123     for (const auto &item : rootItem->childItems){
124         QList<QString> &l = (item->argument == SearchBlacklist::NAME? names : tths);
125 
126         l.push_back(item->title);
127     }
128 
129     SearchBlacklist *SB = SearchBlacklist::getInstance();
130     SB->setList(SearchBlacklist::NAME, names);
131     SB->setList(SearchBlacklist::TTH, tths);
132 }
133 
rowCount(const QModelIndex &) const134 int SearchBlackListModel::rowCount(const QModelIndex & ) const {
135     return rootItem->childCount();
136 }
137 
columnCount(const QModelIndex &) const138 int SearchBlackListModel::columnCount(const QModelIndex & ) const {
139     return 2;
140 }
141 
flags(const QModelIndex & index) const142 Qt::ItemFlags SearchBlackListModel::flags(const QModelIndex &index) const {
143     if (!index.isValid())
144         return 0;
145 
146     return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
147 }
148 
data(const QModelIndex & index,int role) const149 QVariant SearchBlackListModel::data(const QModelIndex & index, int role) const {
150     if (!index.isValid())
151         return QVariant();
152 
153     SearchBlackListItem * item = static_cast<SearchBlackListItem*>(index.internalPointer());
154 
155     if (!item)
156         return QVariant();
157 
158     switch (role){
159         case Qt::DisplayRole:
160         {
161             if (!index.column())
162                 return item->title;
163             else
164                 return (item->argument == SearchBlacklist::NAME? tr("Filename") : tr("TTH"));
165 
166             break;
167         }
168         default:
169             break;
170     }
171 
172     return QVariant();
173 }
174 
175 
headerData(int section,Qt::Orientation orientation,int role) const176 QVariant SearchBlackListModel::headerData(int section, Qt::Orientation orientation, int role) const {
177     if ((orientation == Qt::Horizontal) && (role == Qt::DisplayRole)) {
178         switch (section) {
179             case 0: return tr("Key");
180             case 1: return tr("Type");
181         }
182     }
183 
184     return QVariant();
185 }
186 
data(int column) const187 QVariant SearchBlackListItem::data(int column) const {
188     if (column == COLUMN_SBL_KEY && !childItems.isEmpty() && parentItem)
189         return childItems.size()+1;
190 
191     switch(column){
192         case COLUMN_SBL_TYPE:
193             return QVariant (argument);
194         default:
195             return QVariant (title);
196     }
197 }
198 
199 namespace {
200     template <Qt::SortOrder order>
201     struct Compare {
sort__anon9544dc530111::Compare202         void static sort(int col, QList<SearchBlackListItem*>& items) {
203             qStableSort(items.begin(), items.end(), getAttrComp(col));
204         }
205 
insertSorted__anon9544dc530111::Compare206         void static insertSorted(int col, QList<SearchBlackListItem*>& items, SearchBlackListItem* item) {
207             auto it = qLowerBound(items.begin(), items.end(), item, getAttrComp(col));
208             items.insert(it, item);
209         }
210 
211         private:
212             typedef bool (*AttrComp)(const SearchBlackListItem * l, const SearchBlackListItem * r);
getAttrComp__anon9544dc530111::Compare213             AttrComp static getAttrComp(int column) {
214                 switch (column){
215                     case COLUMN_SBL_TYPE:
216                         return AttrCmp<COLUMN_SBL_TYPE>;
217                     default:
218                         return AttrCmp<COLUMN_SBL_KEY>;
219                 }
220             }
221             template <int i>
AttrCmp__anon9544dc530111::Compare222             bool static AttrCmp(const SearchBlackListItem * l, const SearchBlackListItem * r) {
223                 return Cmp(QString::localeAwareCompare(l->data(i).toString(), r->data(i).toString()), 0);
224             }
225             template <typename T, T (SearchBlackListItem::*attr)>
AttrCmp__anon9544dc530111::Compare226             bool static AttrCmp(const SearchBlackListItem * l, const SearchBlackListItem * r) {
227                 return Cmp(l->*attr, r->*attr);
228             }
229             template <int i>
NumCmp__anon9544dc530111::Compare230             bool static NumCmp(const SearchBlackListItem * l, const SearchBlackListItem * r) {
231                 return Cmp(l->data(i).toULongLong(), r->data(i).toULongLong());
232             }
233             template <typename T>
234             bool static Cmp(const T& l, const T& r);
235     };
236 
237     template <> template <typename T>
Cmp(const T & l,const T & r)238     bool inline Compare<Qt::AscendingOrder>::Cmp(const T& l, const T& r) {
239         return l < r;
240     }
241 
242     template <> template <typename T>
Cmp(const T & l,const T & r)243     bool inline Compare<Qt::DescendingOrder>::Cmp(const T& l, const T& r) {
244         return l > r;
245     }
246 } //namespace
247 
sort(int column,Qt::SortOrder order)248 void SearchBlackListModel::sort(int column, Qt::SortOrder order) {
249     static int c = 0;
250 
251     if (column < 0)
252         column = c;
253 
254     emit layoutAboutToBeChanged();
255 
256     if (order == Qt::AscendingOrder)
257         Compare<Qt::AscendingOrder>().sort(column, rootItem->childItems);
258     else if (order == Qt::DescendingOrder)
259         Compare<Qt::DescendingOrder>().sort(column, rootItem->childItems);
260 
261     c = column;
262 
263     emit layoutChanged();
264 }
265 
index(int row,int column,const QModelIndex &) const266 QModelIndex SearchBlackListModel::index(int row, int column, const QModelIndex &) const {
267     if (row > (rootItem->childCount() - 1) || row < 0)
268         return QModelIndex();
269 
270     return createIndex(row, column, rootItem->child(row));
271 }
272 
parent(const QModelIndex &) const273 QModelIndex SearchBlackListModel::parent(const QModelIndex & ) const {
274     return QModelIndex();
275 }
276 
addEmptyItem()277 QModelIndex SearchBlackListModel::addEmptyItem(){
278     SearchBlackListItem *item = new SearchBlackListItem(rootItem);
279     item->title = tr("Set text...");
280 
281     rootItem->appendChild(item);
282 
283     emit layoutChanged();
284 
285     return createIndex(item->row(), 0, item);
286 }
287 
getSortColumn() const288 int SearchBlackListModel::getSortColumn() const{
289     return sortColumn;
290 }
291 
setSortColumn(int sc)292 void SearchBlackListModel::setSortColumn(int sc){
293     sortColumn = sc;
294 }
295 
SearchBlackListItem(SearchBlackListItem * parent)296 SearchBlackListItem::SearchBlackListItem(SearchBlackListItem *parent) : argument(0), parentItem(parent)
297 {
298 }
299 
~SearchBlackListItem()300 SearchBlackListItem::~SearchBlackListItem()
301 {
302     qDeleteAll(childItems);
303 }
304 
appendChild(SearchBlackListItem * item)305 void SearchBlackListItem::appendChild(SearchBlackListItem *item) {
306     item->parentItem = this;
307     childItems.append(item);
308 }
309 
child(int row)310 SearchBlackListItem *SearchBlackListItem::child(int row) {
311     return childItems.value(row);
312 }
313 
childCount() const314 int SearchBlackListItem::childCount() const {
315     return childItems.count();
316 }
317 
columnCount() const318 int SearchBlackListItem::columnCount() const {
319     return itemData.count();
320 }
parent()321 SearchBlackListItem *SearchBlackListItem::parent() {
322     return parentItem;
323 }
324 
row() const325 int SearchBlackListItem::row() const {
326     if (parentItem)
327         return parentItem->childItems.indexOf(const_cast<SearchBlackListItem*>(this));
328 
329     return 0;
330 }
331 
SearchBlackListDelegate(QObject * parent)332 SearchBlackListDelegate::SearchBlackListDelegate(QObject *parent):
333         QStyledItemDelegate(parent)
334 {
335 }
336 
~SearchBlackListDelegate()337 SearchBlackListDelegate::~SearchBlackListDelegate(){
338 }
339 
createEditor(QWidget * parent,const QStyleOptionViewItem & option,const QModelIndex & index) const340 QWidget *SearchBlackListDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const{
341     if (index.column() == 1){
342         QComboBox *edit = new QComboBox(parent);
343 
344         edit->addItem(tr("Filename"));
345         edit->addItem(tr("TTH"));
346 
347         return edit;
348     }
349     else{
350         SearchBlackListItem *item = reinterpret_cast<SearchBlackListItem*>(index.internalPointer());
351         QLineEdit *edit = new QLineEdit(parent);
352         edit->setText(item->title);
353 
354         return edit;
355     }
356 }
357 
updateEditorGeometry(QWidget * editor,const QStyleOptionViewItem & option,const QModelIndex & index) const358 void SearchBlackListDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const{
359     editor->setGeometry(option.rect);
360 }
361 
setEditorData(QWidget * editor,const QModelIndex & index) const362 void SearchBlackListDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const{
363     SearchBlackListItem *item = reinterpret_cast<SearchBlackListItem*>(index.internalPointer());
364 
365     if (!item)
366         return;
367 
368     if (index.column() == 1){
369         QComboBox *edit = qobject_cast<QComboBox*>(editor);
370 
371         if (!edit)
372             return;
373 
374         edit->setCurrentIndex(item->argument);
375     }
376     else{
377         QLineEdit *edit = qobject_cast<QLineEdit*>(editor);
378 
379         if (!edit)
380             return;
381 
382         edit->setText(item->title);
383     }
384 }
385 
setModelData(QWidget * editor,QAbstractItemModel * model,const QModelIndex & index) const386 void SearchBlackListDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const{
387     SearchBlackListModel *m = qobject_cast<SearchBlackListModel* >(model);
388     SearchBlackListItem *item = reinterpret_cast<SearchBlackListItem* >(index.internalPointer());
389 
390     if (!m)
391         return;
392 
393     if (index.column() == 1){
394         QComboBox *edit = qobject_cast<QComboBox*>(editor);
395 
396         if (!edit || !item)
397             return;
398 
399         item->argument = edit->currentIndex();
400     }
401     else{
402         QLineEdit *edit = qobject_cast<QLineEdit*>(editor);
403 
404         if (!edit || !item)
405             return;
406 
407         item->title = edit->text();
408     }
409 }
410