1
/***************************************************************************
2
* Copyright (C) 2004-2010 by Thomas Fischer *
3
* fischer@unix-ag.uni-kl.de *
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. *
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. *
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
***************************************************************************/
33
#include <bibtexentries.h>
35
#include "bibtexfilemodel.h"
37
static const QRegExp curlyRegExp("[{}]+");
39
void SortFilterBibTeXFileModel::setSourceModel(QAbstractItemModel *model)
41
QSortFilterProxyModel::setSourceModel(model);
42
m_internalModel = dynamic_cast<BibTeXFileModel*>(model);
43
m_bibtexFields = BibTeXFields::self();
46
BibTeXFileModel *SortFilterBibTeXFileModel::bibTeXSourceModel()
48
return m_internalModel;
51
void SortFilterBibTeXFileModel::updateFilter(SortFilterBibTeXFileModel::FilterQuery filterQuery)
53
m_filterQuery = filterQuery;
54
m_filterQuery.field = filterQuery.field.toLower(); /// required for comparison in filter code
58
bool SortFilterBibTeXFileModel::lessThan(const QModelIndex & left, const QModelIndex & right) const
60
int column = left.column();
61
if (column == right.column() && (m_bibtexFields->at(column).upperCamelCase == QLatin1String("Author") || m_bibtexFields->at(column).upperCamelCase == QLatin1String("Editor"))) {
62
/// special sorting for authors or editors: check all names, compare last and then first names
63
Entry *entryA = dynamic_cast<Entry*>(m_internalModel->element(left.row()));
64
Entry *entryB = dynamic_cast<Entry*>(m_internalModel->element(right.row()));
65
if (entryA == NULL || entryB == NULL)
66
return QSortFilterProxyModel::lessThan(left, right);
68
Value valueA = entryA->value(m_bibtexFields->at(column).upperCamelCase);
69
Value valueB = entryB->value(m_bibtexFields->at(column).upperCamelCase);
71
valueA = entryA->value(m_bibtexFields->at(column).upperCamelCaseAlt);
73
valueB = entryB->value(m_bibtexFields->at(column).upperCamelCaseAlt);
75
if (valueA.isEmpty() || valueB.isEmpty())
76
return QSortFilterProxyModel::lessThan(left, right);
78
for (Value::Iterator itA = valueA.begin(), itB = valueB.begin(); itA != valueA.end() && itB != valueB.end(); ++itA, ++itB) {
79
Person *personA = dynamic_cast<Person *>(*itA);
80
Person *personB = dynamic_cast<Person *>(*itB);
81
if (personA == NULL || personB == NULL) return QSortFilterProxyModel::lessThan(left, right);
83
QString nameA = personA->lastName().replace(curlyRegExp, "");
84
QString nameB = personB->lastName().replace(curlyRegExp, "");
85
int cmp = QString::compare(nameA, nameB, Qt::CaseInsensitive);
86
if (cmp < 0) return true;
87
if (cmp > 0) return false;
89
nameA = personA->firstName().replace(curlyRegExp, "");
90
nameB = personB->firstName().replace(curlyRegExp, "");
91
cmp = QString::compare(nameA, nameB, Qt::CaseInsensitive);
92
if (cmp < 0) return true;
93
if (cmp > 0) return false;
95
// TODO Check for suffix and prefix?
98
return QSortFilterProxyModel::lessThan(left, right);
100
return QSortFilterProxyModel::lessThan(left, right);
103
bool SortFilterBibTeXFileModel::filterAcceptsRow(int source_row, const QModelIndex & source_parent) const
105
Q_UNUSED(source_parent)
107
if (m_filterQuery.terms.isEmpty()) return true; /// empty filter query
109
Element *rowElement = m_internalModel->element(source_row);
110
Q_ASSERT(rowElement != NULL);
112
Entry *entry = dynamic_cast<Entry*>(rowElement);
114
/// if current row contains an Entry ...
117
bool *all = new bool[m_filterQuery.terms.count()];
118
for (int i = m_filterQuery.terms.count() - 1; i >= 0; --i)
121
for (Entry::ConstIterator it = entry->constBegin(); it != entry->constEnd(); ++it)
122
if (m_filterQuery.field.isEmpty() || m_filterQuery.field == it.key().toLower()) {
124
for (QStringList::ConstIterator itsl = m_filterQuery.terms.constBegin(); itsl != m_filterQuery.terms.constEnd(); ++itsl, ++i) {
125
bool contains = it.value().containsPattern(*itsl);
132
if (m_filterQuery.field.isEmpty())
133
for (QStringList::ConstIterator itsl = m_filterQuery.terms.constBegin(); itsl != m_filterQuery.terms.constEnd(); ++itsl, ++i) {
134
bool contains = entry->id().contains(*itsl);
140
for (i = m_filterQuery.terms.count() - 1; i >= 0; --i) every &= all[i];
143
if (m_filterQuery.combination == SortFilterBibTeXFileModel::AnyTerm)
148
Macro *macro = dynamic_cast<Macro*>(rowElement);
151
for (QStringList::ConstIterator itsl = m_filterQuery.terms.constBegin(); itsl != m_filterQuery.terms.constEnd(); ++itsl) {
152
bool contains = macro->value().containsPattern(*itsl) || macro->key().contains(*itsl, Qt::CaseInsensitive);
153
if (m_filterQuery.combination == SortFilterBibTeXFileModel::AnyTerm && contains)
159
Comment *comment = dynamic_cast<Comment*>(rowElement);
160
if (comment != NULL) {
162
for (QStringList::ConstIterator itsl = m_filterQuery.terms.constBegin(); itsl != m_filterQuery.terms.constEnd(); ++itsl) {
163
bool contains = comment->text().contains(*itsl, Qt::CaseInsensitive);
164
if (m_filterQuery.combination == SortFilterBibTeXFileModel::AnyTerm && contains)
170
Preamble *preamble = dynamic_cast<Preamble*>(rowElement);
171
if (preamble != NULL) {
173
for (QStringList::ConstIterator itsl = m_filterQuery.terms.constBegin(); itsl != m_filterQuery.terms.constEnd(); ++itsl) {
174
bool contains = preamble->value().containsPattern(*itsl);
175
if (m_filterQuery.combination == SortFilterBibTeXFileModel::AnyTerm && contains)
190
const QRegExp BibTeXFileModel::whiteSpace = QRegExp("(\\s\\n\\r\\t)+");
192
BibTeXFileModel::BibTeXFileModel(QObject * parent)
193
: QAbstractTableModel(parent), m_bibtexFile(NULL)
195
m_bibtexFields = BibTeXFields::self();
199
BibTeXFileModel::~BibTeXFileModel()
201
if (m_bibtexFile != NULL) delete m_bibtexFile;
205
File *BibTeXFileModel::bibTeXFile()
207
if (m_bibtexFile == NULL) m_bibtexFile = new File();
211
void BibTeXFileModel::setBibTeXFile(File *bibtexFile)
213
m_bibtexFile = bibtexFile;
214
reset(); // TODO necessary here?
218
QModelIndex BibTeXFileModel::index(int row, int column, const QModelIndex & parent) const
221
return createIndex(row, column, (void*)NULL); // parent == QModelIndex() ? createIndex(row, column, (void*)NULL) : QModelIndex();
225
QModelIndex BibTeXFileModel::parent(const QModelIndex & index) const
228
return QModelIndex();
231
bool BibTeXFileModel::hasChildren(const QModelIndex & parent) const
233
return parent == QModelIndex();
236
int BibTeXFileModel::rowCount(const QModelIndex & /*parent*/) const
238
return m_bibtexFile != NULL ? m_bibtexFile->count() : 0;
241
int BibTeXFileModel::columnCount(const QModelIndex & /*parent*/) const
243
return m_bibtexFields->count();
246
QVariant BibTeXFileModel::data(const QModelIndex &index, int role) const
248
/// do not accept invalid indices
249
if (!index.isValid())
252
/// check backend storage (File object)
253
if (m_bibtexFile == NULL)
256
/// for now, only display data (no editing or icons etc)
257
if (role != Qt::DisplayRole && role != Qt::ToolTipRole && role != Qt::DecorationRole)
260
if (index.row() < m_bibtexFile->count() && index.column() < m_bibtexFields->count()) {
261
QString raw = m_bibtexFields->at(index.column()).upperCamelCase;
262
QString rawAlt = m_bibtexFields->at(index.column()).upperCamelCaseAlt;
263
Element* element = (*m_bibtexFile)[index.row()];
264
Entry* entry = dynamic_cast<Entry*>(element);
266
/// if BibTeX entry has a "x-color" field, use that color to highlight row
267
if (role == Qt::DecorationRole) {
269
if (index.column() != 0 || entry == NULL || (color = PlainTextValue::text(entry->value("x-color"), m_bibtexFile)) == "#000000" || color.isEmpty())
272
return QVariant(QColor(color));
276
if (raw == "^id") // FIXME: Use constant here?
277
return QVariant(entry->id());
278
else if (raw == "^type") { // FIXME: Use constant here?
279
/// try to beautify type, e.g. translate "proceedings" into
280
/// "Conference or Workshop Proceedings"
281
QString label = BibTeXEntries::self()->label(entry->type());
282
if (label.isEmpty()) {
283
/// fall-back to entry type as it is
284
return QVariant(entry->type());
286
return QVariant(label);
288
if (entry->contains(raw)) {
289
QString text = PlainTextValue::text(entry->value(raw), m_bibtexFile);
290
text = text.replace(whiteSpace, " ");
291
return QVariant(text);
292
} else if (!rawAlt.isNull() && entry->contains(rawAlt)) {
293
QString text = PlainTextValue::text(entry->value(rawAlt), m_bibtexFile);
294
text = text.replace(whiteSpace, " ");
295
return QVariant(text);
300
Macro* macro = dynamic_cast<Macro*>(element);
303
return QVariant(macro->key());
304
else if (raw == "^type")
305
return QVariant(i18n("Macro"));
306
else if (raw == "Title") {
307
QString text = PlainTextValue::text(macro->value(), m_bibtexFile);
308
text = text.replace(whiteSpace, " ");
309
return QVariant(text);
313
Comment* comment = dynamic_cast<Comment*>(element);
314
if (comment != NULL) {
316
return QVariant(i18n("Comment"));
317
else if (raw == Entry::ftTitle) {
318
QString text = comment->text().replace(QRegExp("[\\s\\n\\r\\t]+"), " ");
319
return QVariant(text);
323
Preamble* preamble = dynamic_cast<Preamble*>(element);
324
if (preamble != NULL) {
326
return QVariant(i18n("Preamble"));
327
else if (raw == Entry::ftTitle) {
328
QString text = PlainTextValue::text(preamble->value(), m_bibtexFile);
329
text = text.replace(QRegExp("[\\s\\n\\r\\t]+"), " ");
330
return QVariant(text);
334
return QVariant("?");
339
return QVariant("?");
342
QVariant BibTeXFileModel::headerData(int section, Qt::Orientation orientation, int role) const
344
if (role != Qt::DisplayRole || orientation != Qt::Horizontal || section < 0 || section >= m_bibtexFields->count())
347
return m_bibtexFields->at(section).label;
350
Qt::ItemFlags BibTeXFileModel::flags(const QModelIndex &index) const
353
return Qt::ItemIsEnabled | Qt::ItemIsSelectable; // FIXME: What about drag'n'drop?
356
bool BibTeXFileModel::removeRow(int row, const QModelIndex & parent)
358
if (row < 0 || row >= rowCount() || row >= m_bibtexFile->count())
360
if (parent != QModelIndex())
363
m_bibtexFile->removeAt(row);
370
bool BibTeXFileModel::removeRowList(const QList<int> &rows)
372
QList<int> internalRows = rows;
373
qSort(internalRows.begin(), internalRows.end(), qGreater<int>());
375
foreach(int row, internalRows) {
376
if (row < 0 || row >= rowCount() || row >= m_bibtexFile->count())
378
m_bibtexFile->removeAt(row);
386
bool BibTeXFileModel::insertRow(Element *element, int row, const QModelIndex & parent)
388
if (row < 0 || row > rowCount())
390
if (parent != QModelIndex())
393
m_bibtexFile->insert(row, element);
400
Element* BibTeXFileModel::element(int row) const
402
if (m_bibtexFile == NULL || row < 0 || row >= m_bibtexFile->count()) return NULL;
404
return (*m_bibtexFile)[row];
407
int BibTeXFileModel::row(Element *element) const
409
return m_bibtexFile->indexOf(element);