diff --git a/src/document.h b/src/document.h index 46b08e10..b971a1a0 100644 --- a/src/document.h +++ b/src/document.h @@ -1,253 +1,256 @@ /*************************************************************************** Copyright (C) 2001-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #ifndef TELLICO_DOCUMENT_H #define TELLICO_DOCUMENT_H #include "datavectors.h" #include "filter.h" #include #include #include namespace Tellico { namespace Import { class TellicoImporter; class TellicoSaxImporter; } class MergeConflictResolver { public: enum Result { KeepFirst, KeepSecond, CancelMerge }; MergeConflictResolver() {} virtual ~MergeConflictResolver() {} virtual Result resolve(Data::EntryPtr entry1, Data::EntryPtr entry2, Data::FieldPtr field, const QString& value1 = QString(), const QString& value2 = QString()) = 0; + +private: + Q_DISABLE_COPY(MergeConflictResolver) }; namespace Data { /** * The Document contains everything needed to deal with the contents, thus separated from * the viewer, the Tellico object. It can take of opening and saving documents, and contains * a list of the collections in the document. * * @author Robby Stephenson */ class Document : public QObject { Q_OBJECT public: static Document* self() { if(!s_self) s_self = new Document(); return s_self; } /** * Sets the URL associated with the document. * * @param url The URL */ void setURL(const QUrl& url); /** * Checks the modified flag, which indicates if the document has changed since the * last save. * * @return A boolean indicating the modified status */ bool isModified() const { return m_isModified; } void setModified(bool modified); /** * Sets whether all images are loaded from file or not */ void setLoadAllImages(bool loadAll) { m_loadAllImages = loadAll; } /** * Returns the current url associated with the document * * @return The url */ const QUrl& URL() const { return m_url; } /** * Initializes a new document. The signalNewDoc() signal is emitted. The return * value is currently always true, but should indicate whether or not a new document * was correctly initialized. * * @param type The type of collection to add * @return A boolean indicating success */ bool newDocument(int type); /** * Open a document given a specified location. If, for whatever reason, the file * cannot be opened, a proper message box is shown, indicating the problem. The * signalNewDoc() signal is made once the file contents have been confirmed. * * @param url The location to open * @return A boolean indicating success */ bool openDocument(const QUrl& url); /** * Saves the document contents to a file. * * @param url The location to save the file * @param force Boolean indicating the file should be overwritten if necessary * @return A boolean indicating success */ bool saveDocument(const QUrl& url, bool force = false); /** * Closes the document, deleting the contents. The return value is presently always true. * * @return A boolean indicating success */ bool closeDocument(); /** * Deletes the contents of the document. A signalCollectionDeleted() will be sent for every * collection in the document. */ void deleteContents(); /** * Returns a pointer to the document collection * * @return The collection */ CollPtr collection() const; /** * Returns true if there are no entries. A doc with an empty collection is still empty. */ bool isEmpty() const; /** * Appends the contents of another collection to the current one. The collections must be the * same type. Fields which are in the current collection are left alone. Fields * in the appended collection not in the current one are added. Entries in the appended collection * are added to the current one. * * @param coll A pointer to the appended collection. */ void appendCollection(CollPtr coll); static void appendCollection(CollPtr targetColl, CollPtr sourceColl); /** * Merges another collection into this one. The collections must be the same type. Fields in the * current collection are left alone. Fields not in the current are added. The merging is slow * since each entry in @p coll must be compared to every entry in the current collection. * * @param coll A pointer to the collection to be merged. * @return A QPair of the merged entries, see note in datavectors.h */ MergePair mergeCollection(CollPtr coll); static MergePair mergeCollection(CollPtr targetColl, CollPtr sourceColl); /** * Replace the current collection with a new one. Effectively, this is equivalent to opening * a new file containing this collection. * * @param coll A Pointer to the new collection, the document takes ownership. */ void replaceCollection(CollPtr coll); void unAppendCollection(CollPtr coll, FieldList origFields); void unMergeCollection(CollPtr coll, FieldList origFields_, MergePair entryPair); bool loadAllImagesNow() const; bool allImagesOnDisk() const { return m_allImagesOnDisk; } int imageCount() const; EntryList filteredEntries(FilterPtr filter) const; void renameCollection(const QString& newTitle); void checkInEntry(EntryPtr entry); void checkOutEntry(EntryPtr entry); /** * The second entry vector contains entries with images which should not be removed * in addition to those already in the collection */ void removeImagesNotInCollection(EntryList entries, EntryList entriesToKeep); void cancelImageWriting() { m_cancelImageWriting = true; } static bool mergeEntry(EntryPtr entry1, EntryPtr entry2, MergeConflictResolver* resolver=nullptr); // adds new fields into collection if any values in entries are not empty // first object is modified fields, second is new fields static QPair mergeFields(Data::CollPtr coll, Data::FieldList fields, Data::EntryList entries); public Q_SLOTS: /** * Sets the modified flag to true, emitting signalModified. * */ void slotSetModified(); void slotSetClean(bool clean); Q_SIGNALS: /** * Signals that the document has been modified. */ void signalModified(bool modified); /** * Signals that a status message should be shown. * * @param str The message */ void signalStatusMsg(const QString& str); /** * Signals that all images in the loaded file have been loaded * into memory or onto the disk */ void signalCollectionImagesLoaded(Tellico::Data::CollPtr coll); void signalCollectionAdded(Tellico::Data::CollPtr coll); void signalCollectionDeleted(Tellico::Data::CollPtr coll); private Q_SLOTS: /** * Does an initial loading of all images, used for writing * images to temp dir initially */ void slotLoadAllImages(); private: static Document* s_self; /** * Writes all images in the current collection to the cache directory * if cacheDir = LocalDir, then url will be used and must not be empty */ void writeAllImages(int cacheDir, const QUrl& url=QUrl()); bool pruneImages(); // make all constructors private Document(); Document(const Document& doc); Document& operator=(const Document&); ~Document(); CollPtr m_coll; bool m_isModified; bool m_loadAllImages; QUrl m_url; bool m_validFile; QPointer m_importer; bool m_cancelImageWriting; int m_fileFormat; bool m_allImagesOnDisk; }; } // end namespace } // end namespace #endif diff --git a/src/fetch/messagehandler.h b/src/fetch/messagehandler.h index aea592c4..8b41a956 100644 --- a/src/fetch/messagehandler.h +++ b/src/fetch/messagehandler.h @@ -1,61 +1,64 @@ /*************************************************************************** Copyright (C) 2005-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #ifndef TELLICO_FETCH_MESSAGEHANDLER_H #define TELLICO_FETCH_MESSAGEHANDLER_H #include class QStringList; namespace Tellico { namespace Fetch { /** * @author Robby Stephenson */ class MessageHandler { public: enum Type { Status, Warning, Error, ListError }; MessageHandler() {} virtual ~MessageHandler() {} virtual void send(const QString& message, Type type) = 0; virtual void infoList(const QString& message, const QStringList& list) = 0; + +private: + Q_DISABLE_COPY(MessageHandler) }; class ManagerMessage : public MessageHandler { public: ManagerMessage() : MessageHandler() {} virtual ~ManagerMessage() {} virtual void send(const QString& message, Type type) Q_DECL_OVERRIDE; virtual void infoList(const QString& message, const QStringList& list) Q_DECL_OVERRIDE; }; } // end namespace } // end namespace #endif diff --git a/src/gui/datewidget.cpp b/src/gui/datewidget.cpp index a23ad9b3..724370e2 100644 --- a/src/gui/datewidget.cpp +++ b/src/gui/datewidget.cpp @@ -1,324 +1,328 @@ /*************************************************************************** Copyright (C) 2003-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ // this class borrows heavily from kdateedit.h in the kdepim module // which is Copyright (c) 2002 Cornelius Schumacher // and published under the LGPL #include "datewidget.h" #include "spinbox.h" #include #include #include #include #include #include #include #include #include #include using Tellico::GUI::DateWidget; class DateWidget::DatePickerAction : public QWidgetAction { + Q_OBJECT + public: DatePickerAction( KDatePicker *widget, QObject *parent ) : QWidgetAction( parent ), mDatePicker( widget ), mOriginalParent( widget->parentWidget() ) { } protected: QWidget *createWidget( QWidget *parent ) Q_DECL_OVERRIDE { mDatePicker->setParent( parent ); return mDatePicker; } void deleteWidget( QWidget *widget ) Q_DECL_OVERRIDE { if ( widget != mDatePicker ) { return; } mDatePicker->setParent( mOriginalParent ); } private: KDatePicker *mDatePicker; QWidget *mOriginalParent; }; DateWidget::DateWidget(QWidget* parent_) : QWidget(parent_) { QBoxLayout* l = new QHBoxLayout(this); l->setContentsMargins(0, 0, 0, 0); // 0 allows empty value m_daySpin = new SpinBox(0, 31, this); l->addWidget(m_daySpin, 1); l->setStretchFactor(m_daySpin, 1); m_monthCombo = new KComboBox(false, this); l->addWidget(m_monthCombo, 1); l->setStretchFactor(m_monthCombo, 1); // allow empty item m_monthCombo->addItem(QString()); for(int i = 1; ; ++i) { QString str = QDate::longMonthName(i); if(str.isEmpty()) { break; } m_monthCombo->addItem(str); } m_yearSpin = new SpinBox(QDate::fromJulianDay(0).year(), 9999, this); l->addWidget(m_yearSpin, 1); l->setStretchFactor(m_yearSpin, 1); void (SpinBox::* valueChangedInt)(int) = &SpinBox::valueChanged; void (KComboBox::* activatedInt)(int) = &KComboBox::activated; connect(m_daySpin, valueChangedInt, this, &DateWidget::slotDateChanged); connect(m_monthCombo, activatedInt, this, &DateWidget::slotDateChanged); connect(m_yearSpin, valueChangedInt, this, &DateWidget::slotDateChanged); m_dateButton = new QPushButton(this); m_dateButton->setIcon(QIcon::fromTheme(QStringLiteral("view-pim-calendar"))); connect(m_dateButton, &QAbstractButton::clicked, this, &DateWidget::slotShowPicker); l->addWidget(m_dateButton, 0); m_menu = new QMenu(this); m_menu->hide(); m_picker = new KDatePicker(m_menu); m_picker->setCloseButton(false); connect(m_picker, &KDatePicker::dateEntered, this, &DateWidget::slotDateEntered); connect(m_picker, &KDatePicker::dateSelected, this, &DateWidget::slotDateSelected); m_menu->addAction(new DatePickerAction(m_picker, m_menu)); } DateWidget::~DateWidget() { } void DateWidget::slotDateChanged() { int day = m_daySpin->value(); day = qMin(qMax(day, m_daySpin->minimum()), m_daySpin->maximum()); int m = m_monthCombo->currentIndex(); m = qMin(qMax(m, 0), m_monthCombo->count()-1); int y = m_yearSpin->value(); y = qMin(qMax(y, m_yearSpin->minimum()), m_yearSpin->maximum()); // if all are valid, set this date if(day > m_daySpin->minimum() && m > 0 && y > m_yearSpin->minimum()) { QDate d(y, m, day); setDate(d); } emit signalModified(); } QDate DateWidget::date() const { // possible for either day, month, or year to be empty // in which case a null date is returned int day = m_daySpin->value(); // min value is the empty one if(day == m_daySpin->minimum()) { return QDate(); } int month = m_monthCombo->currentIndex(); if(month == 0) { return QDate(); } int year = m_yearSpin->value(); if(year == m_yearSpin->minimum()) { return QDate(); } return QDate(year, month, day); } QString DateWidget::text() const { // possible for either day, month, or year to be empty // but not all three bool empty = true; // format is "year-month-day" QString s; if(m_yearSpin->value() > m_yearSpin->minimum()) { s += QString::number(m_yearSpin->value()); empty = false; } s += QLatin1Char('-'); // first item is empty if(m_monthCombo->currentIndex() > 0) { // zero-pad to two digits if(m_monthCombo->currentIndex() < 10) { s += QLatin1Char('0'); } s += QString::number(m_monthCombo->currentIndex()); empty = false; } s += QLatin1Char('-'); if(m_daySpin->value() > m_daySpin->minimum()) { // zero-pad to two digits if(m_daySpin->value() < 10) { s += QLatin1Char('0'); } s += QString::number(m_daySpin->value()); empty = false; } return empty ? QString() : s; } void DateWidget::setDate(QDate date_) { m_daySpin->blockSignals(true); m_monthCombo->blockSignals(true); m_yearSpin->blockSignals(true); m_daySpin->setMaximum(date_.daysInMonth()); m_daySpin->setValue(date_.day()); m_monthCombo->setCurrentIndex(date_.month()); // don't subtract 1 since there's the blank first item m_yearSpin->setValue(date_.year()); m_daySpin->blockSignals(false); m_monthCombo->blockSignals(false); m_yearSpin->blockSignals(false); } void DateWidget::setDate(const QString& date_) { m_daySpin->blockSignals(true); m_monthCombo->blockSignals(true); m_yearSpin->blockSignals(true); QStringList s = date_.split(QLatin1Char('-')); bool ok = true; int y = s.count() > 0 ? s[0].toInt(&ok) : m_yearSpin->minimum(); if(!ok) { y = m_yearSpin->minimum(); ok = true; } y = qMin(qMax(y, m_yearSpin->minimum()), m_yearSpin->maximum()); m_yearSpin->setValue(y); int m = s.count() > 1 ? s[1].toInt(&ok) : 0; if(!ok) { m = 0; ok = true; } m = qMin(qMax(m, 0), m_monthCombo->count()-1); m_monthCombo->setCurrentIndex(m); // need to update number of days in month // for now set date to 1 QDate date(y, (m == 0 ? 1 : m), 1); m_daySpin->blockSignals(true); m_daySpin->setMaximum(date.daysInMonth()); m_daySpin->blockSignals(false); int day = s.count() > 2 ? s[2].toInt(&ok) : m_daySpin->minimum(); if(!ok) { day = m_daySpin->minimum(); } day = qMin(qMax(day, m_daySpin->minimum()), m_daySpin->maximum()); m_daySpin->setValue(day); m_daySpin->blockSignals(false); m_monthCombo->blockSignals(false); m_yearSpin->blockSignals(false); // if all are valid, set this date if(day > m_daySpin->minimum() && m > 0 && y > m_yearSpin->minimum()) { QDate d(y, m, day); m_picker->blockSignals(true); m_picker->setDate(d); m_picker->blockSignals(false); } } void DateWidget::clear() { m_daySpin->blockSignals(true); m_monthCombo->blockSignals(true); m_yearSpin->blockSignals(true); m_picker->blockSignals(true); m_daySpin->setValue(m_daySpin->minimum()); m_monthCombo->setCurrentIndex(0); m_yearSpin->setValue(m_yearSpin->minimum()); m_picker->setDate(QDate::currentDate()); m_daySpin->blockSignals(false); m_monthCombo->blockSignals(false); m_yearSpin->blockSignals(false); m_picker->blockSignals(false); } void DateWidget::slotShowPicker() { QRect desk = QApplication::desktop()->screenGeometry(this); QPoint popupPoint = mapToGlobal(QPoint(0, 0)); int dateFrameHeight = m_menu->sizeHint().height(); if(popupPoint.y() + height() + dateFrameHeight > desk.bottom()) { popupPoint.setY(popupPoint.y() - dateFrameHeight); } else { popupPoint.setY(popupPoint.y() + height()); } int dateFrameWidth = m_menu->sizeHint().width(); if(popupPoint.x() + width() > desk.right()) { popupPoint.setX(desk.right() - dateFrameWidth); } else { popupPoint.setX(popupPoint.x() + width() - dateFrameWidth); } if(popupPoint.x() < desk.left()) { popupPoint.setX( desk.left()); } if(popupPoint.y() < desk.top()) { popupPoint.setY(desk.top()); } QDate d = date(); if(d.isValid()) { m_picker->setDate(d); } m_menu->popup(popupPoint); } void DateWidget::slotDateSelected(QDate date_) { if(date_.isValid()) { setDate(date_); emit signalModified(); m_menu->hide(); } } void DateWidget::slotDateEntered(QDate date_) { if(date_.isValid()) { setDate(date_); emit signalModified(); } } + +#include "datewidget.moc" diff --git a/src/images/imagedirectory.h b/src/images/imagedirectory.h index 0878bb0f..a8888eb8 100644 --- a/src/images/imagedirectory.h +++ b/src/images/imagedirectory.h @@ -1,106 +1,109 @@ /*************************************************************************** Copyright (C) 2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #ifndef TELLICO_IMAGEDIRECTORY_H #define TELLICO_IMAGEDIRECTORY_H #include "../utils/stringset.h" #include class QTemporaryDir; class KZip; class KArchiveDirectory; namespace Tellico { namespace Data { class Image; } class ImageStorage { public: ImageStorage() {} virtual ~ImageStorage() {} virtual bool hasImage(const QString& id) = 0; virtual Data::Image* imageById(const QString& id) = 0; + +private: + Q_DISABLE_COPY(ImageStorage) }; class ImageDirectory : public ImageStorage { public: ImageDirectory(); ImageDirectory(const QString& path); virtual ~ImageDirectory(); virtual QString path(); virtual void setPath(const QString& path); bool hasImage(const QString& id) Q_DECL_OVERRIDE; Data::Image* imageById(const QString& id) Q_DECL_OVERRIDE; bool writeImage(const Data::Image& image); bool removeImage(const QString& id); private: Q_DISABLE_COPY(ImageDirectory) QString m_path; bool m_pathExists; // until the file gets saved, the local directory is temporary QTemporaryDir* m_dir; }; class TemporaryImageDirectory : public ImageDirectory { public: TemporaryImageDirectory(); virtual ~TemporaryImageDirectory(); virtual QString path() Q_DECL_OVERRIDE; void purge(); private: Q_DISABLE_COPY(TemporaryImageDirectory) void setPath(const QString& path) Q_DECL_OVERRIDE; QTemporaryDir* m_dir; }; class ImageZipArchive : public ImageStorage { public: ImageZipArchive(); virtual ~ImageZipArchive(); void setZip(KZip* zip); bool hasImage(const QString& id) Q_DECL_OVERRIDE; Data::Image* imageById(const QString& id) Q_DECL_OVERRIDE; private: Q_DISABLE_COPY(ImageZipArchive) KZip* m_zip; const KArchiveDirectory* m_imgDir; StringSet m_images; }; } // end namespace #endif diff --git a/src/models/fieldcomparison.h b/src/models/fieldcomparison.h index 82b81cfe..0e19739c 100644 --- a/src/models/fieldcomparison.h +++ b/src/models/fieldcomparison.h @@ -1,92 +1,93 @@ /*************************************************************************** Copyright (C) 2007-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #ifndef TELLICO_FIELDCOMPARISON_H #define TELLICO_FIELDCOMPARISON_H #include "../datavectors.h" #include namespace Tellico { class StringComparison; class FieldComparison { public: FieldComparison(Data::FieldPtr field); virtual ~FieldComparison() {} Data::FieldPtr field() const { return m_field; } virtual int compare(Data::EntryPtr entry1, Data::EntryPtr entry2); static FieldComparison* create(Data::FieldPtr field); protected: virtual int compare(const QString& str1, const QString& str2) = 0; private: + Q_DISABLE_COPY(FieldComparison) Data::FieldPtr m_field; }; class ValueComparison : public FieldComparison { public: ValueComparison(Data::FieldPtr field, StringComparison* comp); ~ValueComparison(); using FieldComparison::compare; protected: virtual int compare(const QString& str1, const QString& str2) Q_DECL_OVERRIDE; private: StringComparison* m_stringComparison; }; class ImageComparison : public FieldComparison { public: ImageComparison(Data::FieldPtr field); using FieldComparison::compare; protected: virtual int compare(const QString& str1, const QString& str2) Q_DECL_OVERRIDE; }; class ChoiceComparison : public FieldComparison { public: ChoiceComparison(Data::FieldPtr field); using FieldComparison::compare; protected: virtual int compare(const QString& str1, const QString& str2) Q_DECL_OVERRIDE; private: QStringList m_values; }; } #endif diff --git a/src/models/stringcomparison.h b/src/models/stringcomparison.h index b0108849..716bc30a 100644 --- a/src/models/stringcomparison.h +++ b/src/models/stringcomparison.h @@ -1,78 +1,81 @@ /*************************************************************************** Copyright (C) 2007-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #ifndef TELLICO_STRINGCOMPARISON_H #define TELLICO_STRINGCOMPARISON_H #include #include "../datavectors.h" namespace Tellico { class StringComparison { public: StringComparison(); virtual ~StringComparison() {} virtual int compare(const QString& str1, const QString& str2); static StringComparison* create(Data::FieldPtr field); + +private: + Q_DISABLE_COPY(StringComparison) }; class BoolComparison : public StringComparison { public: BoolComparison(); virtual int compare(const QString& str1, const QString& str2) Q_DECL_OVERRIDE; }; class TitleComparison : public StringComparison { public: TitleComparison(); virtual int compare(const QString& str1, const QString& str2) Q_DECL_OVERRIDE; }; class NumberComparison : public StringComparison { public: NumberComparison(); virtual int compare(const QString& str1, const QString& str2) Q_DECL_OVERRIDE; }; class LCCComparison : public StringComparison { public: LCCComparison(); virtual int compare(const QString& str1, const QString& str2) Q_DECL_OVERRIDE; private: int compareLCC(const QStringList& cap1, const QStringList& cap2) const; QRegExp m_regexp; }; class ISODateComparison : public StringComparison { public: ISODateComparison(); virtual int compare(const QString& str1, const QString& str2) Q_DECL_OVERRIDE; }; } #endif diff --git a/src/tests/allocinefetchertest.cpp b/src/tests/allocinefetchertest.cpp index 4b42a4c3..cdbffe86 100644 --- a/src/tests/allocinefetchertest.cpp +++ b/src/tests/allocinefetchertest.cpp @@ -1,238 +1,238 @@ /*************************************************************************** Copyright (C) 2010-2012 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #undef QT_NO_CAST_FROM_ASCII #include "allocinefetchertest.h" #include "../fetch/execexternalfetcher.h" #include "../fetch/allocinefetcher.h" #include "../collections/videocollection.h" #include "../collectionfactory.h" #include "../entry.h" #include "../images/imagefactory.h" #include #include #include QTEST_GUILESS_MAIN( AllocineFetcherTest ) AllocineFetcherTest::AllocineFetcherTest() : AbstractFetcherTest() { } void AllocineFetcherTest::initTestCase() { Tellico::RegisterCollection registerVideo(Tellico::Data::Collection::Video, "video"); Tellico::ImageFactory::init(); } void AllocineFetcherTest::cleanupTestCase() { Tellico::ImageFactory::clean(true); } void AllocineFetcherTest::testTitle() { // Allocine script is currently failing return; Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Video, Tellico::Fetch::Title, QStringLiteral("Superman Returns")); Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::ExecExternalFetcher(this)); KConfig config(QFINDTESTDATA("../fetch/scripts/fr.allocine.py.spec"), KConfig::SimpleConfig); KConfigGroup cg = config.group(QStringLiteral("")); cg.writeEntry("ExecPath", QFINDTESTDATA("../fetch/scripts/fr.allocine.py")); // don't sync() and save the new path cg.markAsClean(); fetcher->readConfig(cg, cg.name()); Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1); QCOMPARE(results.size(), 1); Tellico::Data::EntryPtr entry = results.at(0); QCOMPARE(entry->field(QStringLiteral("title")), QStringLiteral("Superman Returns")); QCOMPARE(entry->field(QStringLiteral("director")), QStringLiteral("Bryan Singer")); QCOMPARE(entry->field(QStringLiteral("producer")), QStringLiteral("Jon Peters; Gilbert Adler; Bryan Singer; Lorne Orleans")); QCOMPARE(entry->field(QStringLiteral("studio")), QStringLiteral("Warner Bros. France")); QCOMPARE(entry->field(QStringLiteral("year")), QStringLiteral("2006")); QCOMPARE(entry->field(QStringLiteral("genre")), QStringLiteral("Fantastique; Action")); QCOMPARE(entry->field(QStringLiteral("nationality")), QString::fromUtf8("Américain; Australien")); QCOMPARE(entry->field(QStringLiteral("running-time")), QStringLiteral("154")); QStringList castList = Tellico::FieldFormat::splitTable(entry->field(QStringLiteral("cast"))); QVERIFY(!castList.isEmpty()); QCOMPARE(castList.at(0), QStringLiteral("Brandon Routh::Clark Kent / Superman")); QCOMPARE(castList.size(), 8); QVERIFY(!entry->field(QStringLiteral("plot")).isEmpty()); QVERIFY(!entry->field(QStringLiteral("cover")).isEmpty()); QVERIFY(!entry->field(QStringLiteral("cover")).contains(QLatin1Char('/'))); } void AllocineFetcherTest::testTitleAccented() { // Allocine script is currently failing return; Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Video, Tellico::Fetch::Title, QStringLiteral("Opération Tonnerre")); Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::ExecExternalFetcher(this)); KConfig config(QFINDTESTDATA("../fetch/scripts/fr.allocine.py.spec"), KConfig::SimpleConfig); KConfigGroup cg = config.group(QStringLiteral("")); cg.writeEntry("ExecPath", QFINDTESTDATA("../fetch/scripts/fr.allocine.py")); // don't sync() and save the new path cg.markAsClean(); fetcher->readConfig(cg, cg.name()); Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1); QCOMPARE(results.size(), 1); Tellico::Data::EntryPtr entry = results.at(0); QCOMPARE(entry->field(QStringLiteral("title")), QString::fromUtf8("Opération Tonnerre")); QCOMPARE(entry->field(QStringLiteral("titre-original")), QStringLiteral("Thunderball")); - QCOMPARE(entry->field(QStringLiteral("studio")), QStringLiteral("")); + QCOMPARE(entry->field(QStringLiteral("studio")), QString()); } void AllocineFetcherTest::testTitleAccentRemoved() { // Allocine script is currently failing return; Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Video, Tellico::Fetch::Title, QStringLiteral("Operation Tonnerre")); Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::ExecExternalFetcher(this)); KConfig config(QFINDTESTDATA("../fetch/scripts/fr.allocine.py.spec"), KConfig::SimpleConfig); KConfigGroup cg = config.group(QStringLiteral("")); cg.writeEntry("ExecPath", QFINDTESTDATA("../fetch/scripts/fr.allocine.py")); // don't sync() and save the new path cg.markAsClean(); fetcher->readConfig(cg, cg.name()); Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1); QCOMPARE(results.size(), 1); Tellico::Data::EntryPtr entry = results.at(0); QCOMPARE(entry->field(QStringLiteral("title")), QString::fromUtf8("Opération Tonnerre")); } void AllocineFetcherTest::testPlotQuote() { // Allocine script is currently failing return; Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Video, Tellico::Fetch::Title, QStringLiteral("Goldfinger")); Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::ExecExternalFetcher(this)); KConfig config(QFINDTESTDATA("../fetch/scripts/fr.allocine.py.spec"), KConfig::SimpleConfig); KConfigGroup cg = config.group(QStringLiteral("")); cg.writeEntry("ExecPath", QFINDTESTDATA("../fetch/scripts/fr.allocine.py")); // don't sync() and save the new path cg.markAsClean(); fetcher->readConfig(cg, cg.name()); Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1); QCOMPARE(results.size(), 1); Tellico::Data::EntryPtr entry = results.at(0); QCOMPARE(entry->field(QStringLiteral("title")), QStringLiteral("Goldfinger")); QVERIFY(!entry->field(QStringLiteral("plot")).contains(QStringLiteral("""))); } void AllocineFetcherTest::testTitleAPI() { KConfig config(QFINDTESTDATA("tellicotest.config"), KConfig::SimpleConfig); QString groupName = QStringLiteral("allocine"); if(!config.hasGroup(groupName)) { QSKIP("This test requires a config file.", SkipAll); } KConfigGroup cg(&config, groupName); Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Video, Tellico::Fetch::Keyword, QStringLiteral("Superman Returns")); Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::AllocineFetcher(this)); fetcher->readConfig(cg, cg.name()); Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1); QCOMPARE(results.size(), 1); Tellico::Data::EntryPtr entry = results.at(0); QCOMPARE(entry->field(QStringLiteral("title")), QStringLiteral("Superman Returns")); QCOMPARE(entry->field(QStringLiteral("director")), QStringLiteral("Bryan Singer")); QCOMPARE(entry->field(QStringLiteral("producer")), QStringLiteral("Jon Peters; Gilbert Adler; Bryan Singer; Lorne Orleans")); QCOMPARE(entry->field(QStringLiteral("studio")), QStringLiteral("Warner Bros. France")); QCOMPARE(entry->field(QStringLiteral("year")), QStringLiteral("2006")); QCOMPARE(entry->field(QStringLiteral("genre")), QStringLiteral("Fantastique; Action")); QCOMPARE(entry->field(QStringLiteral("nationality")), QStringLiteral("U.S.A.; Australie")); QCOMPARE(entry->field(QStringLiteral("running-time")), QStringLiteral("154")); QStringList castList = Tellico::FieldFormat::splitTable(entry->field(QStringLiteral("cast"))); QVERIFY(!castList.isEmpty()); QCOMPARE(castList.at(0), QStringLiteral("Brandon Routh::Clark Kent / Superman")); QCOMPARE(castList.size(), 5); QVERIFY(!entry->field(QStringLiteral("plot")).isEmpty()); QVERIFY(!entry->field(QStringLiteral("cover")).isEmpty()); QVERIFY(!entry->field(QStringLiteral("cover")).contains(QLatin1Char('/'))); } void AllocineFetcherTest::testTitleAPIAccented() { KConfig config(QFINDTESTDATA("tellicotest.config"), KConfig::SimpleConfig); QString groupName = QStringLiteral("allocine"); if(!config.hasGroup(groupName)) { QSKIP("This test requires a config file.", SkipAll); } KConfigGroup cg(&config, groupName); Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Video, Tellico::Fetch::Keyword, QStringLiteral("Opération Tonnerre")); Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::AllocineFetcher(this)); fetcher->readConfig(cg, cg.name()); Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1); QCOMPARE(results.size(), 1); Tellico::Data::EntryPtr entry = results.at(0); QCOMPARE(entry->field(QStringLiteral("title")), QString::fromUtf8("Opération Tonnerre")); QCOMPARE(entry->field(QStringLiteral("origtitle")), QStringLiteral("Thunderball")); - QCOMPARE(entry->field(QStringLiteral("studio")), QStringLiteral("")); + QCOMPARE(entry->field(QStringLiteral("studio")), QString()); QCOMPARE(entry->field(QStringLiteral("director")), QStringLiteral("Terence Young")); QCOMPARE(entry->field(QStringLiteral("color")), QStringLiteral("Color")); QVERIFY(!entry->field(QStringLiteral("allocine")).isEmpty()); } // mentioned in https://bugs.kde.org/show_bug.cgi?id=337432 void AllocineFetcherTest::testGhostDog() { KConfig config(QFINDTESTDATA("tellicotest.config"), KConfig::SimpleConfig); QString groupName = QStringLiteral("allocine"); if(!config.hasGroup(groupName)) { QSKIP("This test requires a config file.", SkipAll); } KConfigGroup cg(&config, groupName); Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Video, Tellico::Fetch::Keyword, QStringLiteral("Ghost Dog: la voie du samourai")); Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::AllocineFetcher(this)); fetcher->readConfig(cg, cg.name()); Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1); QCOMPARE(results.size(), 1); Tellico::Data::EntryPtr entry = results.at(0); QCOMPARE(entry->field(QStringLiteral("title")), QStringLiteral("Ghost Dog: la voie du samourai")); } diff --git a/src/tests/dblpfetchertest.cpp b/src/tests/dblpfetchertest.cpp index 63ef3f92..d9916be6 100644 --- a/src/tests/dblpfetchertest.cpp +++ b/src/tests/dblpfetchertest.cpp @@ -1,91 +1,91 @@ /*************************************************************************** Copyright (C) 2012 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #undef QT_NO_CAST_FROM_ASCII #include "dblpfetchertest.h" #include "../fetch/dblpfetcher.h" #include "../collections/bibtexcollection.h" #include "../collectionfactory.h" #include "../entry.h" #include "../utils/datafileregistry.h" #include QTEST_GUILESS_MAIN( DBLPFetcherTest ) DBLPFetcherTest::DBLPFetcherTest() : AbstractFetcherTest() { } void DBLPFetcherTest::initTestCase() { Tellico::RegisterCollection registerBibtex(Tellico::Data::Collection::Bibtex, "bibtex"); Tellico::DataFileRegistry::self()->addDataLocation(QFINDTESTDATA("../../xslt/dblp2tellico.xsl")); } void DBLPFetcherTest::testProceedings() { Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Bibtex, Tellico::Fetch::Keyword, QStringLiteral("Chip and PIN is Broken")); Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::DBLPFetcher(this)); Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1); QCOMPARE(results.size(), 1); Tellico::Data::EntryPtr entry = results.at(0); QCOMPARE(entry->field(QStringLiteral("title")), QStringLiteral("Chip and PIN is Broken.")); QCOMPARE(entry->field(QStringLiteral("author")), QStringLiteral("Steven J. Murdoch; Saar Drimer; Ross J. Anderson; Mike Bond")); QCOMPARE(entry->field(QStringLiteral("year")), QStringLiteral("2010")); QCOMPARE(entry->field(QStringLiteral("pages")), QStringLiteral("433-446")); QCOMPARE(entry->field(QStringLiteral("booktitle")), QStringLiteral("IEEE Symposium on Security and Privacy")); - QCOMPARE(entry->field(QStringLiteral("journal")), QStringLiteral("")); + QCOMPARE(entry->field(QStringLiteral("journal")), QString()); QCOMPARE(entry->field(QStringLiteral("url")), QStringLiteral("https://dblp.org/rec/conf/sp/MurdochDAB10")); // QCOMPARE(entry->field(QStringLiteral("doi")), QStringLiteral("10.1109/SP.2010.33")); QCOMPARE(entry->field(QStringLiteral("entry-type")), QStringLiteral("inproceedings")); QCOMPARE(entry->field(QStringLiteral("bibtex-key")), QStringLiteral("MurdochDAB10")); } void DBLPFetcherTest::testArticle() { Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Bibtex, Tellico::Fetch::Keyword, QStringLiteral("Nontrivial independent sets of bipartite graphs")); Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::DBLPFetcher(this)); Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1); QCOMPARE(results.size(), 1); Tellico::Data::EntryPtr entry = results.at(0); QCOMPARE(entry->field(QStringLiteral("title")), QStringLiteral("Nontrivial independent sets of bipartite graphs and cross-intersecting families.")); QCOMPARE(entry->field(QStringLiteral("author")), QStringLiteral("Jun Wang; Huajun Zhang")); QCOMPARE(entry->field(QStringLiteral("year")), QStringLiteral("2013")); QCOMPARE(entry->field(QStringLiteral("pages")), QStringLiteral("129-141")); QCOMPARE(entry->field(QStringLiteral("volume")), QStringLiteral("120")); QCOMPARE(entry->field(QStringLiteral("number")), QStringLiteral("1")); QCOMPARE(entry->field(QStringLiteral("journal")), QStringLiteral("J. Comb. Theory, Ser. A")); - QCOMPARE(entry->field(QStringLiteral("booktitle")), QStringLiteral("")); + QCOMPARE(entry->field(QStringLiteral("booktitle")), QString()); QCOMPARE(entry->field(QStringLiteral("url")), QStringLiteral("https://dblp.org/rec/journals/jct/WangZ13")); // QCOMPARE(entry->field(QStringLiteral("doi")), QStringLiteral("10.1016/j.jcta.2012.07.005")); QCOMPARE(entry->field(QStringLiteral("entry-type")), QStringLiteral("article")); QCOMPARE(entry->field(QStringLiteral("bibtex-key")), QStringLiteral("WangZ13")); } diff --git a/src/tests/entitytest.cpp b/src/tests/entitytest.cpp index b5286387..8492ae2c 100644 --- a/src/tests/entitytest.cpp +++ b/src/tests/entitytest.cpp @@ -1,118 +1,138 @@ /*************************************************************************** Copyright (C) 2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #undef QT_NO_CAST_FROM_ASCII #include "entitytest.h" #include "../utils/string_utils.h" #include QTEST_APPLESS_MAIN( EntityTest ) #define QSL(x) QStringLiteral(x) void EntityTest::testEntities() { QFETCH(QByteArray, data); QFETCH(QString, expectedString); QCOMPARE(Tellico::decodeHTML(data), expectedString); } void EntityTest::testEntities_data() { QTest::addColumn("data"); QTest::addColumn("expectedString"); QTest::newRow("robby") << QByteArray("robby") << QSL("robby"); QTest::newRow("&fake;") << QByteArray("&fake;") << QSL("&fake;"); QTest::newRow("0") << QByteArray("0") << QSL("0"); QTest::newRow("robby0robb") << QByteArray("robby0robby") << QSL("robby0robby"); } void EntityTest::testAccents() { QFETCH(QString, inputString); QFETCH(QString, expectedString); QCOMPARE(Tellico::removeAccents(inputString), expectedString); } void EntityTest::testAccents_data() { QTest::addColumn("inputString"); QTest::addColumn("expectedString"); QTest::newRow("robby") << QSL("robby") << QSL("robby"); QTest::newRow("jose") << QSL("José Guzmán") << QSL("Jose Guzman"); QTest::newRow("inarritu") << QSL("Alejandro González Iñárritu") << QSL("Alejandro Gonzalez Inarritu"); QTest::newRow("harakiri") << QSL("'Shitsurakuen': jôbafuku onna harakiri") << QSL("'Shitsurakuen': jobafuku onna harakiri"); QTest::newRow("svet") << QSL("Tmavomodrý Svět") << QSL("Tmavomodry Svet"); QTest::newRow("russian") << QSL("Возвращение Супермена") << QSL("Возвращение Супермена"); QTest::newRow("chinese") << QSL("湖南科学技术出版社") << QSL("湖南科学技术出版社"); } void EntityTest::testI18nReplace() { QFETCH(QString, inputString); QFETCH(QString, expectedString); QCOMPARE(Tellico::i18nReplace(inputString), expectedString); } void EntityTest::testI18nReplace_data() { QTest::addColumn("inputString"); QTest::addColumn("expectedString"); QTest::newRow("robby") << QSL("robby") << QSL("robby"); QTest::newRow("basic1") << QSL("robby") << QSL("robby"); QTest::newRow("basic2") << QSL("robby davy") << QSL("robby davy"); QTest::newRow("basic3") << QSL("\n robby \n davy\n") << QSL("robby davy\n"); // KDE bug 254863 QTest::newRow("bug254863") << QSL("Cer&ca") << QSL("Cer&ca"); QTest::newRow("multiple") << QSL("robby davy jason") << QSL("robby davy jason"); QTest::newRow("bracket") << QSL("robby ") << QSL("robby <robby>"); } void EntityTest::testMinutes() { QFETCH(int, seconds); QFETCH(QString, minutesString); QCOMPARE(Tellico::minutes(seconds), minutesString); } void EntityTest::testMinutes_data() { QTest::addColumn("seconds"); QTest::addColumn("minutesString"); QTest::newRow("1") << 1 << QSL("0:01"); QTest::newRow("60") << 60 << QSL("1:00"); QTest::newRow("600") << 600 << QSL("10:00"); QTest::newRow("0") << 0 << QSL("0:00"); QTest::newRow("120") << 120 << QSL("2:00"); } void EntityTest::testObfuscate() { QString s(QStringLiteral("1q!Q+=% f")); QByteArray b = Tellico::obfuscate(s); // qDebug() << s << b; QCOMPARE(s, Tellico::reverseObfuscate(b)); } + +void EntityTest::testControlCodes() { + QFETCH(QString, string); + QFETCH(QString, result); + + QCOMPARE(Tellico::removeControlCodes(string), result); +} + +void EntityTest::testControlCodes_data() { + QTest::addColumn("string"); + QTest::addColumn("result"); + + QTest::newRow("basic test") << QSL("basic test") << QSL("basic test"); + QTest::newRow("basic test1") << (QSL("basic test") + QChar(1)) << QSL("basic test"); + QTest::newRow("nl") << QSL("new\nline") << QSL("new\nline"); + QTest::newRow("cr") << QSL("new\rline") << QSL("new\rline"); + QTest::newRow("tab") << QSL("new\ttab") << QSL("new\ttab"); + QTest::newRow("hex5") << QSL("hex5\x5") << QSL("hex5"); + QTest::newRow("hexD") << QSL("hexD\xD") << QSL("hexD\xD"); +} diff --git a/src/tests/entitytest.h b/src/tests/entitytest.h index 5ce0da94..6dae4acf 100644 --- a/src/tests/entitytest.h +++ b/src/tests/entitytest.h @@ -1,45 +1,47 @@ /*************************************************************************** Copyright (C) 2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #ifndef ENTITYTEST_H #define ENTITYTEST_H #include class EntityTest : public QObject { Q_OBJECT private Q_SLOTS: void testEntities(); void testEntities_data(); void testAccents(); void testAccents_data(); void testI18nReplace(); void testI18nReplace_data(); void testMinutes(); void testMinutes_data(); void testObfuscate(); + void testControlCodes(); + void testControlCodes_data(); }; #endif diff --git a/src/tests/filelistingtest.cpp b/src/tests/filelistingtest.cpp index 9858eef0..6efe13d2 100644 --- a/src/tests/filelistingtest.cpp +++ b/src/tests/filelistingtest.cpp @@ -1,114 +1,114 @@ /*************************************************************************** Copyright (C) 2015 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #undef QT_NO_CAST_FROM_ASCII #include #include "filelistingtest.h" #include "../translators/filelistingimporter.h" #include "../translators/xmphandler.h" #include "../images/imagefactory.h" #include // KIO::listDir in FileListingImporter seems to require a GUI Application QTEST_MAIN( FileListingTest ) void FileListingTest::initTestCase() { Tellico::ImageFactory::init(); } void FileListingTest::testCpp() { QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("filelistingtest.cpp")); Tellico::Import::FileListingImporter importer(url.adjusted(QUrl::RemoveFilename)); // can't import images for local test // importer.setOptions(importer.options() & ~Tellico::Import::ImportShowImageErrors); Tellico::Data::CollPtr coll = importer.collection(); QVERIFY(coll); QCOMPARE(coll->type(), Tellico::Data::Collection::File); QVERIFY(coll->entryCount() > 0); Tellico::Data::EntryPtr entry; foreach(Tellico::Data::EntryPtr tmpEntry, coll->entries()) { if(tmpEntry->field(QStringLiteral("title")) == QStringLiteral("filelistingtest.cpp")) { entry = tmpEntry; } } QVERIFY(entry); QCOMPARE(entry->field("title"), QStringLiteral("filelistingtest.cpp")); QCOMPARE(entry->field("url"), url.url()); QVERIFY(entry->field("description").contains("C++")); - QCOMPARE(entry->field("folder"), QStringLiteral("")); // empty relative folder location + QCOMPARE(entry->field("folder"), QString()); // empty relative folder location QCOMPARE(entry->field("mimetype"), QStringLiteral("text/x-c++src")); QVERIFY(!entry->field("size").isEmpty()); QVERIFY(!entry->field("permissions").isEmpty()); QVERIFY(!entry->field("owner").isEmpty()); QVERIFY(!entry->field("group").isEmpty()); // for some reason, the Creation time isn't populated for this test // QVERIFY(!entry->field("created").isEmpty()); QVERIFY(!entry->field("modified").isEmpty()); #ifdef HAVE_KFILEMETADATA QCOMPARE(entry->field("metainfo"), QString()); #endif // icon name does not get set for the jenkins build service // QVERIFY(!entry->field("icon").isEmpty()); } void FileListingTest::testXMPData() { { Tellico::XMPHandler xmp; #ifdef HAVE_EXEMPI QVERIFY(xmp.isXMPEnabled()); QVERIFY(!xmp.extractXMP(QFINDTESTDATA("data/BlueSquare.jpg")).isEmpty()); #endif } // initializing exempi can cause a crash in Exiv for files with XMP data // see https://bugs.kde.org/show_bug.cgi?id=390744 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("data/BlueSquare.jpg")); Tellico::Import::FileListingImporter importer(url.adjusted(QUrl::RemoveFilename)); Tellico::Data::CollPtr coll = importer.collection(); QVERIFY(coll); QCOMPARE(coll->type(), Tellico::Data::Collection::File); QVERIFY(coll->entryCount() > 0); Tellico::Data::EntryPtr entry; foreach(Tellico::Data::EntryPtr tmpEntry, coll->entries()) { if(tmpEntry->field(QStringLiteral("title")) == QStringLiteral("BlueSquare.jpg")) { entry = tmpEntry; } } QVERIFY(entry); QCOMPARE(entry->field("title"), QStringLiteral("BlueSquare.jpg")); QCOMPARE(entry->field("mimetype"), QStringLiteral("image/jpeg")); QCOMPARE(entry->field("size"), QStringLiteral("23.6 KiB")); #ifdef HAVE_KFILEMETADATA #ifdef HAVE_EXEMPI QEXPECT_FAIL("", "Because of a crash related to exempi and kfilemetadata linking, no metadata is read", Continue); #endif QVERIFY(!entry->field("metainfo").isEmpty()); #endif } diff --git a/src/translators/amcimporter.cpp b/src/translators/amcimporter.cpp index 65ffe5d6..e9f8ea6a 100644 --- a/src/translators/amcimporter.cpp +++ b/src/translators/amcimporter.cpp @@ -1,317 +1,318 @@ /*************************************************************************** Copyright (C) 2006-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ // The information about the AMC file format was taken from the source code for // GCfilms, (GPL) (c) 2005 Tian // Monotheka, (GPL) (c) 2004, 2005 Michael Dominic K. // 2005 Aurelien Mino #include "amcimporter.h" #include "../fieldformat.h" #include "../collections/videocollection.h" #include "../images/imagefactory.h" #include "../tellico_debug.h" #include #include #include #include +#define AMC_FILE_ID " AMC_X.Y Ant Movie Catalog 3.5.x www.buypin.com www.antp.be " + namespace { - static const QByteArray AMC_FILE_ID = " AMC_X.Y Ant Movie Catalog 3.5.x www.buypin.com www.antp.be "; static const quint32 AMC_MAX_STRING_SIZE = 128 * 1024; } using Tellico::Import::AMCImporter; AMCImporter::AMCImporter(const QUrl& url_) : DataImporter(url_), m_cancelled(false), m_failed(false), m_majVersion(0), m_minVersion(0) { } AMCImporter::~AMCImporter() { } bool AMCImporter::canImport(int type) const { return type == Data::Collection::Video; } Tellico::Data::CollPtr AMCImporter::collection() { if(m_coll) { return m_coll; } if(!fileRef().open()) { return Data::CollPtr(); } QIODevice* f = fileRef().file(); m_ds.setDevice(f); // AMC is always little-endian? can't confirm m_ds.setByteOrder(QDataStream::LittleEndian); emit signalTotalSteps(this, f->size()); - const uint l = AMC_FILE_ID.length(); + const uint l = sizeof(AMC_FILE_ID)-1; QVector buffer(l+1); m_ds.readRawData(buffer.data(), l); QString version = QString::fromLocal8Bit(buffer.data(), l); QRegExp versionRx(QLatin1String(".+AMC_(\\d+)\\.(\\d+).+")); if(versionRx.indexIn(version) == -1) { myDebug() << "no file id match"; return Data::CollPtr(); } m_coll = new Data::VideoCollection(true); m_majVersion = versionRx.cap(1).toInt(); m_minVersion = versionRx.cap(2).toInt(); // myDebug() << m_majVersion << "-" << m_minVersion; readString(); // name readString(); // email if(m_majVersion <= 3 && m_minVersion < 5) { readString(); // icq } readString(); // webpage readString(); // description const bool showProgress = options() & ImportProgress; while(!m_cancelled && !m_failed && !f->atEnd()) { readEntry(); if(showProgress) { emit signalProgress(this, f->pos()); qApp->processEvents(); } } return m_coll; } bool AMCImporter::readBool() { quint8 b; m_ds >> b; return b; } quint32 AMCImporter::readInt() { if(m_failed) { return 0; } quint32 i; m_ds >> i; if(i >= UINT_MAX) { i = 0; } return i; } QString AMCImporter::readString() { if(m_failed) { return QString(); } // The serialization format is a length specifier first, then l bytes of data quint32 l = readInt(); if(l == 0) { return QString(); } if(l > AMC_MAX_STRING_SIZE) { myDebug() << "string is too long:" << l; m_failed = true; return QString(); } QVector buffer(l+1); m_ds.readRawData(buffer.data(), l); QString s = QString::fromLocal8Bit(buffer.data(), l); // myDebug() << "string: " << s; return s; } QString AMCImporter::readImage(const QString& format_) { if(m_failed) { return QString(); } quint32 l = readInt(); if(l == 0) { return QString(); } if(l > AMC_MAX_STRING_SIZE) { myDebug() << "string is too long:" << l; m_failed = true; return QString(); } QVector buffer(l+1); m_ds.readRawData(buffer.data(), l); QByteArray bytes; bytes.reserve(l); qCopy(buffer.data(), buffer.data() + l, bytes.begin()); QImage img = QImage::fromData(bytes); if(img.isNull()) { myDebug() << "null image"; return QString(); } QString format = QStringLiteral("PNG"); if(format_ == QLatin1String(".jpg")) { format = QStringLiteral("JPEG"); } else if(format_ == QLatin1String(".gif")) { format = QStringLiteral("GIF"); } return ImageFactory::addImage(img, format); } void AMCImporter::readEntry() { Data::EntryPtr e(new Data::Entry(m_coll)); quint32 id = readInt(); if(id > 0) { e->setId(id); } readInt(); // add date quint32 rating = readInt(); if(m_majVersion >= 3 && m_minVersion >= 5) { rating /= 10; } e->setField(QStringLiteral("rating"), QString::number(rating)); quint32 year = readInt(); if(year > 0) { e->setField(QStringLiteral("year"), QString::number(year)); } quint32 time = readInt(); if(time > 0) { e->setField(QStringLiteral("running-time"), QString::number(time)); } readInt(); // video bitrate readInt(); // audio bitrate readInt(); // number of files readBool(); // checked readString(); // media label e->setField(QStringLiteral("medium"), readString()); readString(); // source readString(); // borrower QString s = readString(); // title if(!s.isEmpty()) { e->setField(QStringLiteral("title"), s); } QString s2 = readString(); // translated title if(s.isEmpty()) { e->setField(QStringLiteral("title"), s2); } e->setField(QStringLiteral("director"), readString()); s = readString(); QRegExp roleRx(QLatin1String("(.+) \\(([^(]+)\\)")); roleRx.setMinimal(true); if(roleRx.indexIn(s) > -1) { QString role = roleRx.cap(2).toLower(); if(role == QLatin1String("story") || role == QLatin1String("written by")) { e->setField(QStringLiteral("writer"), roleRx.cap(1)); } else { e->setField(QStringLiteral("producer"), s); } } else { e->setField(QStringLiteral("producer"), s); } e->setField(QStringLiteral("nationality"), readString()); e->setField(QStringLiteral("genre"), readString().replace(QLatin1String(", "), FieldFormat::delimiterString())); e->setField(QStringLiteral("cast"), parseCast(readString()).join(FieldFormat::rowDelimiterString())); readString(); // url e->setField(QStringLiteral("plot"), readString()); e->setField(QStringLiteral("comments"), readString()); s = readString(); // video format QRegExp regionRx(QLatin1String("Region \\d")); if(regionRx.indexIn(s) > -1) { e->setField(QStringLiteral("region"), regionRx.cap(0)); } e->setField(QStringLiteral("audio-track"), readString()); // audio format readString(); // resolution readString(); // frame rate e->setField(QStringLiteral("language"), readString()); // audio language e->setField(QStringLiteral("subtitle"), readString()); // subtitle readString(); // file size s = readString(); // picture extension s = readImage(s); // picture if(!s.isEmpty()) { e->setField(QStringLiteral("cover"), s); } m_coll->addEntries(e); } QStringList AMCImporter::parseCast(const QString& text_) { QStringList cast; int nPar = 0; QRegExp castRx(QLatin1String("[,()]")); QString person, role; int oldPos = 0; for(int pos = castRx.indexIn(text_); pos > -1; pos = castRx.indexIn(text_, pos+1)) { if(text_.at(pos) == QLatin1Char(',') && nPar%2 == 0) { // we're done with this one person += text_.mid(oldPos, pos-oldPos).trimmed(); QString all = person; if(!role.isEmpty()) { if(role.startsWith(QLatin1String("as "))) { role = role.mid(3); } all += FieldFormat::columnDelimiterString() + role; } cast << all; person.clear(); role.clear(); oldPos = pos+1; // add one to go past comma } else if(text_.at(pos) == QLatin1Char('(')) { if(nPar == 0) { person = text_.mid(oldPos, pos-oldPos).trimmed(); oldPos = pos+1; // add one to go past parenthesis } ++nPar; } else if(text_.at(pos) == QLatin1Char(')')) { --nPar; if(nPar == 0) { role = text_.mid(oldPos, pos-oldPos).trimmed(); oldPos = pos+1; // add one to go past parenthesis } } } // grab the last one if(nPar%2 == 0) { int pos = text_.length(); person += text_.mid(oldPos, pos-oldPos).trimmed(); QString all = person; if(!role.isEmpty()) { if(role.startsWith(QLatin1String("as "))) { role = role.mid(3); } all += FieldFormat::columnDelimiterString() + role; } cast << all; } return cast; } void AMCImporter::slotCancel() { m_cancelled = true; } diff --git a/src/translators/textimporter.cpp b/src/translators/textimporter.cpp index 1111ed64..e332902c 100644 --- a/src/translators/textimporter.cpp +++ b/src/translators/textimporter.cpp @@ -1,49 +1,41 @@ /*************************************************************************** Copyright (C) 2003-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #include "textimporter.h" #include "../core/filehandler.h" - -#include - -namespace { - QString& cleanXml(QString s) { - // remove C0 Control Characters, since we assume we're importing a file - // with contents that will be represented in XML later... - static const QRegExp rx(QLatin1String("[\x0000-\x00FF]")); - return s.remove(rx); - } -} +#include "../utils/string_utils.h" using Tellico::Import::TextImporter; TextImporter::TextImporter(const QUrl& url_, bool useUTF8_) : Import::Importer(url_) { if(url_.isValid()) { - setText(cleanXml(FileHandler::readTextFile(url_, false, useUTF8_))); + // remove C0 Control Characters, since we assume we're importing a file + // with contents that will be represented in XML later... + setText(Tellico::removeControlCodes(FileHandler::readTextFile(url_, false, useUTF8_))); } } TextImporter::TextImporter(const QString& text_) : Import::Importer(text_) { } diff --git a/src/translators/xmlstatehandler.h b/src/translators/xmlstatehandler.h index 9bfa96be..fb2f1124 100644 --- a/src/translators/xmlstatehandler.h +++ b/src/translators/xmlstatehandler.h @@ -1,352 +1,353 @@ /*************************************************************************** Copyright (C) 2008-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #ifndef TELLICO_IMPORT_XMLSTATEHANDLER_H #define TELLICO_IMPORT_XMLSTATEHANDLER_H #include #include "../datavectors.h" namespace Tellico { namespace Import { namespace SAX { class StateData { public: StateData() : syntaxVersion(0), collType(0), defaultFields(false), loadImages(false), hasImages(false), showImageLoadErrors(true) {} QString text; QString error; QString ns; // namespace QString textBuffer; uint syntaxVersion; QString collTitle; int collType; QString entryName; Data::CollPtr coll; Data::FieldList fields; Data::FieldPtr currentField; Data::EntryList entries; QString modifiedDate; FilterPtr filter; Data::BorrowerPtr borrower; bool defaultFields; bool loadImages; bool hasImages; bool showImageLoadErrors; }; class StateHandler { public: StateHandler(StateData* data) : d(data) {} virtual ~StateHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) = 0; virtual bool end(const QString&, const QString&, const QString&) = 0; StateHandler* nextHandler(const QString&, const QString&, const QString&); protected: StateData* d; private: + Q_DISABLE_COPY(StateHandler) virtual StateHandler* nextHandlerImpl(const QString&, const QString&, const QString&) { return nullptr; } }; class NullHandler : public StateHandler { public: NullHandler(StateData* data) : StateHandler(data) {} virtual ~NullHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE { return true; } virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE { return true; } }; class RootHandler : public StateHandler { public: RootHandler(StateData* data) : StateHandler(data) {} virtual ~RootHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE { return true; } virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE { return true; } private: virtual StateHandler* nextHandlerImpl(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; }; class DocumentHandler : public StateHandler { public: DocumentHandler(StateData* data) : StateHandler(data) {} virtual ~DocumentHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; private: virtual StateHandler* nextHandlerImpl(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; }; class CollectionHandler : public StateHandler { public: CollectionHandler(StateData* data) : StateHandler(data) {} virtual ~CollectionHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; private: virtual StateHandler* nextHandlerImpl(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; }; class FieldsHandler : public StateHandler { public: FieldsHandler(StateData* data) : StateHandler(data) {} virtual ~FieldsHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; private: virtual StateHandler* nextHandlerImpl(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; }; class FieldHandler : public StateHandler { public: FieldHandler(StateData* data) : StateHandler(data) {} virtual ~FieldHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; private: virtual StateHandler* nextHandlerImpl(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; }; class FieldPropertyHandler : public StateHandler { public: FieldPropertyHandler(StateData* data) : StateHandler(data) {} virtual ~FieldPropertyHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; private: QString m_propertyName; }; class BibtexPreambleHandler : public StateHandler { public: BibtexPreambleHandler(StateData* data) : StateHandler(data) {} virtual ~BibtexPreambleHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; }; class BibtexMacrosHandler : public StateHandler { public: BibtexMacrosHandler(StateData* data) : StateHandler(data) {} virtual ~BibtexMacrosHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; private: virtual StateHandler* nextHandlerImpl(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; }; class BibtexMacroHandler : public StateHandler { public: BibtexMacroHandler(StateData* data) : StateHandler(data) {} virtual ~BibtexMacroHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; private: QString m_macroName; }; class EntryHandler : public StateHandler { public: EntryHandler(StateData* data) : StateHandler(data) {} virtual ~EntryHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; private: virtual StateHandler* nextHandlerImpl(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; }; class FieldValueContainerHandler : public StateHandler { public: FieldValueContainerHandler(StateData* data) : StateHandler(data) {} virtual ~FieldValueContainerHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; private: virtual StateHandler* nextHandlerImpl(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; }; class FieldValueHandler : public StateHandler { public: FieldValueHandler(StateData* data) : StateHandler(data) , m_i18n(false), m_validateISBN(false) {} virtual ~FieldValueHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; private: virtual StateHandler* nextHandlerImpl(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; bool m_i18n; bool m_validateISBN; }; class DateValueHandler : public StateHandler { public: DateValueHandler(StateData* data) : StateHandler(data) {} virtual ~DateValueHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; }; class TableColumnHandler : public StateHandler { public: TableColumnHandler(StateData* data) : StateHandler(data) {} virtual ~TableColumnHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; }; class ImagesHandler : public StateHandler { public: ImagesHandler(StateData* data) : StateHandler(data) {} virtual ~ImagesHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; private: virtual StateHandler* nextHandlerImpl(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; }; class ImageHandler : public StateHandler { public: ImageHandler(StateData* data) : StateHandler(data) , m_link(false), m_width(0), m_height(0) {} virtual ~ImageHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; private: QString m_format; bool m_link; QString m_imageId; int m_width; int m_height; }; class FiltersHandler : public StateHandler { public: FiltersHandler(StateData* data) : StateHandler(data) {} virtual ~FiltersHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; private: virtual StateHandler* nextHandlerImpl(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; }; class FilterHandler : public StateHandler { public: FilterHandler(StateData* data) : StateHandler(data) {} virtual ~FilterHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; private: virtual StateHandler* nextHandlerImpl(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; }; class FilterRuleHandler : public StateHandler { public: FilterRuleHandler(StateData* data) : StateHandler(data) {} virtual ~FilterRuleHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; }; class BorrowersHandler : public StateHandler { public: BorrowersHandler(StateData* data) : StateHandler(data) {} virtual ~BorrowersHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; private: virtual StateHandler* nextHandlerImpl(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; }; class BorrowerHandler : public StateHandler { public: BorrowerHandler(StateData* data) : StateHandler(data) {} virtual ~BorrowerHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; private: virtual StateHandler* nextHandlerImpl(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; }; class LoanHandler : public StateHandler { public: LoanHandler(StateData* data) : StateHandler(data) , m_id(-1), m_inCalendar(false) {} virtual ~LoanHandler() {} virtual bool start(const QString&, const QString&, const QString&, const QXmlAttributes&) Q_DECL_OVERRIDE; virtual bool end(const QString&, const QString&, const QString&) Q_DECL_OVERRIDE; private: int m_id; QString m_uid; QString m_loanDate; QString m_dueDate; bool m_inCalendar; }; } } } #endif diff --git a/src/utils/gradient.cpp b/src/utils/gradient.cpp index 001a174c..7446c725 100644 --- a/src/utils/gradient.cpp +++ b/src/utils/gradient.cpp @@ -1,601 +1,601 @@ -/* +/* Copyright (C) 1998, 1999, 2001, 2002, 2004, 2005, 2007 Daniel M. Duley (C) 2004 Zack Rusin (C) 2000 Josef Weidendorfer (C) 1999 Geert Jansen (C) 1998, 1999 Christian Tibirna (C) 1998, 1999 Dirk Mueller Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Diagonal gradient code was inspired by BlackBox. BlackBox gradients are (C) Brad Hughes, and Mike Cole . */ #include "gradient.h" #include #include #include -QImage Tellico::gradient(const QSize &size, const QColor &ca, +QImage Tellico::gradient(QSize size, const QColor &ca, const QColor &cb, Tellico::GradientType eff) { QImage image(size, QImage::Format_RGB32); if(!size.isValid()) return(image); int rca, gca, bca, rcb, gcb, bcb; int rDiff = (rcb = cb.red()) - (rca = ca.red()); int gDiff = (gcb = cb.green()) - (gca = ca.green()); int bDiff = (bcb = cb.blue()) - (bca = ca.blue()); int x, y; QRgb rgb; if(eff == VerticalGradient || eff == HorizontalGradient){ int rl = rca << 16; int gl = gca << 16; int bl = bca << 16; QRgb *p; if(eff == VerticalGradient){ int rcdelta = ((1<<16) / size.height()) * rDiff; int gcdelta = ((1<<16) / size.height()) * gDiff; int bcdelta = ((1<<16) / size.height()) * bDiff; for(y=0; y < size.height(); ++y){ rl += rcdelta; gl += gcdelta; bl += bcdelta; rgb = qRgb( (rl>>16), (gl>>16), (bl>>16) ); p = (QRgb *)image.scanLine(y); for(x = 0; x < size.width(); ++x) *p++ = rgb; } } else{ // must be HorizontalGradient int rcdelta = ((1<<16) / size.width()) * rDiff; int gcdelta = ((1<<16) / size.width()) * gDiff; int bcdelta = ((1<<16) / size.width()) * bDiff; p = (QRgb *)image.scanLine(0); for(x = 0; x < size.width(); ++x){ rl += rcdelta; gl += gcdelta; bl += bcdelta; *p++ = qRgb((rl>>16), (gl>>16), (bl>>16)); } p = (QRgb *)image.scanLine(0); for(y = 1; y < size.height(); ++y) memcpy(image.scanLine(y), p, size.width()*sizeof(QRgb)); } } else{ float rfd, gfd, bfd; float rd = rca, gd = gca, bd = bca; int w = size.width(), h = size.height(); int dw = w*2, dh = h*2; unsigned char *xtable = new unsigned char[w*3]; unsigned char *ytable = new unsigned char[h*3]; if(eff == DiagonalGradient || eff == CrossDiagonalGradient){ rfd = (float)rDiff/dw; gfd = (float)gDiff/dw; bfd = (float)bDiff/dw; int dir; for(x=0; x < w; x++, rd+=rfd, gd+=gfd, bd+=bfd) { dir = eff == DiagonalGradient? x : w - x - 1; xtable[dir*3] = (unsigned char) rd; xtable[dir*3+1] = (unsigned char) gd; xtable[dir*3+2] = (unsigned char) bd; } rfd = (float)rDiff/dh; gfd = (float)gDiff/dh; bfd = (float)bDiff/dh; rd = gd = bd = 0; for(y = 0; y < h; y++, rd+=rfd, gd+=gfd, bd+=bfd){ ytable[y*3] = (unsigned char) rd; ytable[y*3+1] = (unsigned char) gd; ytable[y*3+2] = (unsigned char) bd; } for(y = 0; y < h; y++){ QRgb *p = (QRgb *)image.scanLine(y); for(x = 0; x < w; x++){ *p++ = qRgb(xtable[x*3] + ytable[y*3], xtable[x*3+1] + ytable[y*3+1], xtable[x*3+2] + ytable[y*3+2]); } } } else{ int rSign = rDiff>0? 1: -1; int gSign = gDiff>0? 1: -1; int bSign = bDiff>0? 1: -1; rfd = (float)rDiff/w; gfd = (float)gDiff/w; bfd = (float)bDiff/w; rd = (float)rDiff/2; gd = (float)gDiff/2; bd = (float)bDiff/2; for(x=0; x < w; x++, rd-=rfd, gd-=gfd, bd-=bfd){ xtable[x*3] = (unsigned char) qAbs((int)rd); xtable[x*3+1] = (unsigned char) qAbs((int)gd); xtable[x*3+2] = (unsigned char) qAbs((int)bd); } rfd = (float)rDiff/h; gfd = (float)gDiff/h; bfd = (float)bDiff/h; rd = (float)rDiff/2; gd = (float)gDiff/2; bd = (float)bDiff/2; for(y=0; y < h; y++, rd-=rfd, gd-=gfd, bd-=bfd){ ytable[y*3] = (unsigned char) qAbs((int)rd); ytable[y*3+1] = (unsigned char) qAbs((int)gd); ytable[y*3+2] = (unsigned char) qAbs((int)bd); } dw = (w+1)>>1; dh = (h+1)>>1; int x2; QRgb *sl1, *sl2; for(y = 0; y < dh; y++){ sl1 = (QRgb *)image.scanLine(y); sl2 = (QRgb *)image.scanLine(qMax(h-y-1, y)); for(x = 0, x2 = w-1; x < dw; x++, x2--){ switch(eff){ case PyramidGradient: rgb = qRgb(rcb-rSign*(xtable[x*3]+ytable[y*3]), gcb-gSign*(xtable[x*3+1]+ytable[y*3+1]), bcb-bSign*(xtable[x*3+2]+ytable[y*3+2])); break; case RectangleGradient: rgb = qRgb(rcb - rSign * qMax(xtable[x*3], ytable[y*3]) * 2, gcb - gSign * qMax(xtable[x*3+1], ytable[y*3+1]) * 2, bcb - bSign * qMax(xtable[x*3+2], ytable[y*3+2]) * 2); break; case PipeCrossGradient: rgb = qRgb(rcb - rSign * qMin(xtable[x*3], ytable[y*3]) * 2, gcb - gSign * qMin(xtable[x*3+1], ytable[y*3+1]) * 2, bcb - bSign * qMin(xtable[x*3+2], ytable[y*3+2]) * 2); break; case EllipticGradient: default: rgb = qRgb(rcb - rSign * (int)std::sqrt((xtable[x*3]*xtable[x*3] + ytable[y*3]*ytable[y*3])*2.0f), gcb - gSign * (int)std::sqrt((xtable[x*3+1]*xtable[x*3+1] + ytable[y*3+1]*ytable[y*3+1])*2.0f), bcb - bSign * (int)std::sqrt((xtable[x*3+2]*xtable[x*3+2] + ytable[y*3+2]*ytable[y*3+2])*2.0f)); break; } sl1[x] = sl2[x] = rgb; sl1[x2] = sl2[x2] = rgb; } } } delete [] xtable; delete [] ytable; } return(image); } -QImage Tellico::grayGradient(const QSize &size, unsigned char ca, - unsigned char cb, Tellico::GradientType eff) +QImage Tellico::grayGradient(QSize size, unsigned char ca, + unsigned char cb, Tellico::GradientType eff) { QImage image(size, QImage::Format_Indexed8); if(!size.isValid()) return(image); QVector colorTable(256); for(int i=0; i < 256; ++i) colorTable[i] = qRgba(i, i, i, 255); image.setColorTable(colorTable); int diff = cb - ca; int x, y; unsigned char idx; if(eff == VerticalGradient || eff == HorizontalGradient){ int val = ca << 16; unsigned char *p; if(eff == VerticalGradient){ int delta = ((1<<16) / size.height()) * diff; for(y=0; y < size.height(); ++y){ val += delta; idx = val >> 16; p = image.scanLine(y); for(x = 0; x < size.width(); ++x) *p++ = idx; } } else{ // must be HorizontalGradient int delta = ((1<<16) / size.width()) * diff; p = image.scanLine(0); for(x = 0; x < size.width(); ++x){ val += delta; *p++ = val >> 16; } p = image.scanLine(0); for(y = 1; y < size.height(); ++y) memcpy(image.scanLine(y), p, image.bytesPerLine()); } } else{ float delta, val=ca; unsigned int w = size.width(), h = size.height(); unsigned char *xtable = new unsigned char[w]; unsigned char *ytable = new unsigned char[h]; w*=2, h*=2; if(eff == DiagonalGradient || eff == CrossDiagonalGradient){ delta = (float)diff/w; int dir; for(x=0; x < size.width(); x++, val+=delta){ dir = eff == DiagonalGradient? x : size.width() - x - 1; xtable[dir] = (unsigned char) val; } delta = (float)diff/h; val = 0; for(y = 0; y < size.height(); y++, val+=delta) ytable[y] = (unsigned char) val; for(y = 0; y < size.height(); y++){ unsigned char *p = image.scanLine(y); for(x = 0; x < size.width(); x++) *p++ = xtable[x] + ytable[y]; } } else{ int sign = diff>0? 1: -1; delta = (float)diff / size.width(); val = (float)diff/2; for(x=0; x < size.width(); x++, val-=delta) xtable[x] = (unsigned char) qAbs((int)val); delta = (float)diff/size.height(); val = (float)diff/2; for(y=0; y < size.height(); y++, val-=delta) ytable[y] = (unsigned char) qAbs((int)val); int w = (size.width()+1)>>1; int h = (size.height()+1)>>1; int x2; unsigned char *sl1, *sl2; for(y = 0; y < h; y++){ sl1 = image.scanLine(y); sl2 = image.scanLine(qMax(size.height()-y-1, y)); for(x = 0, x2 = size.width()-1; x < w; x++, x2--){ switch(eff){ case PyramidGradient: idx = cb-sign*(xtable[x]+ytable[y]); break; case RectangleGradient: idx = cb-sign*qMax(xtable[x], ytable[y])*2; break; case PipeCrossGradient: idx = cb-sign*qMin(xtable[x], ytable[y])*2; break; case EllipticGradient: default: idx = cb - sign * (int)std::sqrt((xtable[x]*xtable[x] + ytable[y]*ytable[y])*2.0f); break; } sl1[x] = sl2[x] = idx; sl1[x2] = sl2[x2] = idx; } } } delete [] xtable; delete [] ytable; } return(image); } -QImage Tellico::unbalancedGradient(const QSize &size, const QColor &ca, - const QColor &cb, Tellico::GradientType eff, - int xfactor, int yfactor) +QImage Tellico::unbalancedGradient(QSize size, const QColor &ca, + const QColor &cb, Tellico::GradientType eff, + int xfactor, int yfactor) { QImage image(size, QImage::Format_RGB32); if(!size.isValid()) return image; int dir; // general parameter used for direction switches bool _xanti = (xfactor < 0); // negative on X direction bool _yanti = (yfactor < 0); // negative on Y direction xfactor = qBound(1, qAbs(xfactor), 200); yfactor = qBound(1, qAbs(yfactor), 200); // float xbal = xfactor/5000.; // float ybal = yfactor/5000.; float xbal = xfactor/30.0f/size.width(); float ybal = yfactor/30.0f/size.height(); float rat; int x, y; int rca, gca, bca, rcb, gcb, bcb; int rDiff = (rcb = cb.red()) - (rca = ca.red()); int gDiff = (gcb = cb.green()) - (gca = ca.green()); int bDiff = (bcb = cb.blue()) - (bca = ca.blue()); if(eff == VerticalGradient || eff == HorizontalGradient){ QRgb *p; if(eff == VerticalGradient){ QRgb rgbRow; for(y=0; y < size.height(); y++){ dir = _yanti ? y : size.height() - 1 - y; rat = 1 - std::exp( - (float)y * ybal ); p = (QRgb *) image.scanLine(dir); rgbRow = qRgb(rcb - (int) ( rDiff * rat ), gcb - (int) ( gDiff * rat ), bcb - (int) ( bDiff * rat )); for(x = 0; x < size.width(); x++) *p++ = rgbRow; } } else{ p = (QRgb *)image.scanLine(0); for(x = 0; x < size.width(); x++){ dir = _xanti ? x : size.width() - 1 - x; rat = 1 - std::exp( - (float)x * xbal ); p[dir] = qRgb(rcb - (int) ( rDiff * rat ), gcb - (int) ( gDiff * rat ), bcb - (int) ( bDiff * rat )); } p = (QRgb *)image.scanLine(0); for(y = 1; y < size.height(); ++y){ memcpy(image.scanLine(y), p, size.width()*sizeof(QRgb)); } } } else{ int w=size.width(), h=size.height(); unsigned char *xtable = new unsigned char[w*3]; unsigned char *ytable = new unsigned char[h*3]; QRgb *p; if(eff == DiagonalGradient || eff == CrossDiagonalGradient){ for(x = 0; x < w; x++){ dir = _xanti ? x : w - 1 - x; rat = 1 - std::exp( - (float)x * xbal ); xtable[dir*3] = (unsigned char) ( rDiff/2 * rat ); xtable[dir*3+1] = (unsigned char) ( gDiff/2 * rat ); xtable[dir*3+2] = (unsigned char) ( bDiff/2 * rat ); } for(y = 0; y < h; y++){ dir = _yanti ? y : h - 1 - y; rat = 1 - std::exp( - (float)y * ybal ); ytable[dir*3] = (unsigned char) ( rDiff/2 * rat ); ytable[dir*3+1] = (unsigned char) ( gDiff/2 * rat ); ytable[dir*3+2] = (unsigned char) ( bDiff/2 * rat ); } for(y = 0; y < h; y++){ p = (QRgb *)image.scanLine(y); for(x = 0; x < w; x++){ *p++ = qRgb(rcb - (xtable[x*3] + ytable[y*3]), gcb - (xtable[x*3+1] + ytable[y*3+1]), bcb - (xtable[x*3+2] + ytable[y*3+2])); } } } else{ int rSign = rDiff>0? 1: -1; int gSign = gDiff>0? 1: -1; int bSign = bDiff>0? 1: -1; for(x = 0; x < w; x++){ dir = _xanti ? x : w - 1 - x; rat = 1 - std::exp( - (float)x * xbal ); xtable[dir*3] = (unsigned char) qAbs((int)(rDiff*(0.5-rat))); xtable[dir*3+1] = (unsigned char) qAbs((int)(gDiff*(0.5-rat))); xtable[dir*3+2] = (unsigned char) qAbs((int)(bDiff*(0.5-rat))); } for(y = 0; y < h; y++){ dir = _yanti ? y : h - 1 - y; rat = 1 - std::exp( - (float)y * ybal ); ytable[dir*3] = (unsigned char) qAbs((int)(rDiff*(0.5-rat))); ytable[dir*3+1] = (unsigned char) qAbs((int)(gDiff*(0.5-rat))); ytable[dir*3+2] = (unsigned char) qAbs((int)(bDiff*(0.5-rat))); } for(y = 0; y < h; y++){ p = (QRgb *)image.scanLine(y); for(x = 0; x < w; x++) { if (eff == PyramidGradient){ *p++ = qRgb(rcb-rSign*(xtable[x*3]+ytable[y*3]), gcb-gSign*(xtable[x*3+1]+ytable[y*3+1]), bcb-bSign*(xtable[x*3+2]+ytable[y*3+2])); } else if (eff == RectangleGradient){ *p++ = qRgb(rcb - rSign * qMax(xtable[x*3], ytable[y*3]) * 2, gcb - gSign * qMax(xtable[x*3+1], ytable[y*3+1]) * 2, bcb - bSign * qMax(xtable[x*3+2], ytable[y*3+2]) * 2); } else if (eff == PipeCrossGradient){ *p++ = qRgb(rcb - rSign * qMin(xtable[x*3], ytable[y*3]) * 2, gcb - gSign * qMin(xtable[x*3+1], ytable[y*3+1]) * 2, bcb - bSign * qMin(xtable[x*3+2], ytable[y*3+2]) * 2); } else if (eff == EllipticGradient){ *p++ = qRgb(rcb - rSign * (int)std::sqrt((xtable[x*3]*xtable[x*3] + ytable[y*3]*ytable[y*3])*2.0), gcb - gSign * (int)std::sqrt((xtable[x*3+1]*xtable[x*3+1] + ytable[y*3+1]*ytable[y*3+1])*2.0), bcb - bSign * (int)std::sqrt((xtable[x*3+2]*xtable[x*3+2] + ytable[y*3+2]*ytable[y*3+2])*2.0)); } } } } delete [] xtable; delete [] ytable; } return(image); } -QImage Tellico::grayUnbalancedGradient(const QSize &size, unsigned char ca, - unsigned char cb, Tellico::GradientType eff, - int xfactor, int yfactor) +QImage Tellico::grayUnbalancedGradient(QSize size, unsigned char ca, + unsigned char cb, Tellico::GradientType eff, + int xfactor, int yfactor) { QImage image(size, QImage::Format_Indexed8); if(!size.isValid()) return(image); QVector colorTable(256); for(int i=0; i < 256; ++i) colorTable[i] = qRgba(i, i, i, 255); image.setColorTable(colorTable); int dir; // general parameter used for direction switches bool _xanti = (xfactor < 0); // negative on X direction bool _yanti = (yfactor < 0); // negative on Y direction xfactor = qBound(1, qAbs(xfactor), 200); yfactor = qBound(1, qAbs(yfactor), 200); float xbal = xfactor/30.0f/size.width(); float ybal = yfactor/30.0f/size.height(); float rat; int x, y; int diff = cb-ca; if(eff == VerticalGradient || eff == HorizontalGradient){ unsigned char *p; if(eff == VerticalGradient){ unsigned char idx; for(y=0; y < size.height(); y++){ dir = _yanti ? y : size.height() - 1 - y; rat = 1 - std::exp( - (float)y * ybal ); p = image.scanLine(dir); idx = cb - (int)( diff * rat ); for(x = 0; x < size.width(); x++) *p++ = idx; } } else{ p = image.scanLine(0); for(x = 0; x < size.width(); x++){ dir = _xanti ? x : size.width() - 1 - x; rat = 1 - std::exp( - (float)x * xbal ); p[dir] = cb - (int)( diff * rat ); } p = image.scanLine(0); for(y = 1; y < size.height(); ++y) memcpy(image.scanLine(y), p, image.bytesPerLine()); } } else{ int w=size.width(), h=size.height(); unsigned char *xtable = new unsigned char[w]; unsigned char *ytable = new unsigned char[h]; unsigned char *p; if(eff == DiagonalGradient || eff == CrossDiagonalGradient){ for(x = 0; x < w; x++){ dir = _xanti ? x : w - 1 - x; rat = 1 - std::exp( - (float)x * xbal ); xtable[dir] = (unsigned char) ( diff/2 * rat ); } for(y = 0; y < h; y++){ dir = _yanti ? y : h - 1 - y; rat = 1 - std::exp( - (float)y * ybal ); ytable[dir] = (unsigned char) ( diff/2 * rat ); } for(y = 0; y < h; y++){ p = image.scanLine(y); for(x = 0; x < w; x++) *p++ = cb - (xtable[x] + ytable[y]); } } else{ int sign = diff>0? 1: -1; for(x = 0; x < w; x++){ dir = _xanti ? x : w - 1 - x; rat = 1 - std::exp( - (float)x * xbal ); xtable[dir] = (unsigned char) qAbs((int)(diff*(0.5-rat))); } for(y = 0; y < h; y++){ dir = _yanti ? y : h - 1 - y; rat = 1 - std::exp( - (float)y * ybal ); ytable[dir] = (unsigned char) qAbs((int)(diff*(0.5-rat))); } for(y = 0; y < h; y++){ p = image.scanLine(y); for(x = 0; x < w; x++) { if (eff == PyramidGradient) *p++ = cb-sign*(xtable[x]+ytable[y]); else if (eff == RectangleGradient) *p++ = cb -sign*qMax(xtable[x], ytable[y])*2; else if (eff == PipeCrossGradient) *p++ = cb-sign*qMin(xtable[x], ytable[y])*2; else if (eff == EllipticGradient) *p++ = cb-sign * (int)std::sqrt((xtable[x]*xtable[x] + ytable[y]*ytable[y])*2.0); } } } delete [] xtable; delete [] ytable; } return(image); } diff --git a/src/utils/gradient.h b/src/utils/gradient.h index 9b0a5bd6..fba16390 100644 --- a/src/utils/gradient.h +++ b/src/utils/gradient.h @@ -1,100 +1,100 @@ #ifndef __BLITZ_H #define __BLITZ_H -/* +/* Copyright (C) 1998, 1999, 2001, 2002, 2004, 2005, 2007 Daniel M. Duley (C) 2004 Zack Rusin (C) 2000 Josef Weidendorfer (C) 1999 Geert Jansen (C) 1998, 1999 Christian Tibirna (C) 1998, 1999 Dirk Mueller Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include namespace Tellico { enum GradientType {VerticalGradient=0, HorizontalGradient, DiagonalGradient, CrossDiagonalGradient, PyramidGradient, RectangleGradient, PipeCrossGradient, EllipticGradient}; /** * Create a gradient from color a to color b of the specified type. * * @param size The desired size of the gradient. * @param ca Color a * @param cb Color b * @param type The type of gradient. * @return The gradient. */ - QImage gradient(const QSize &size, const QColor &ca, + QImage gradient(QSize size, const QColor &ca, const QColor &cb, GradientType type); /** * Creates an 8bit grayscale gradient suitable for use as an alpha channel * using QImage::setAlphaChannel(). * * @param size The desired size of the gradient. * @param ca The grayscale start value. * @param cb The grayscale end value. * @param type The type of gradient. * @return The gradient. */ - QImage grayGradient(const QSize &size, unsigned char ca, + QImage grayGradient(QSize size, unsigned char ca, unsigned char cb, GradientType type); /** * Create an unbalanced gradient. An unbalanced gradient is a gradient * where the transition from color a to color b is not linear, but in this * case exponential. * * @param size The desired size of the gradient. * @param ca Color a * @param cb Color b * @param type The type of gradient. * @param xfactor The x decay length. Use a value between -200 and 200. * @param yfactor The y decay length. * @return The gradient. */ - QImage unbalancedGradient(const QSize &size, const QColor &ca, + QImage unbalancedGradient(QSize size, const QColor &ca, const QColor &cb, GradientType type, int xfactor=100, int yfactor=100); /** * Creates an 8bit grayscale gradient suitable for use as an alpha channel * using QImage::setAlphaChannel(). * * @param size The desired size of the gradient. * @param type The type of gradient. * @param ca The grayscale start value. * @param cb The grayscale end value. * @param xfactor The x decay length. Use a value between -200 and 200. * @param yfactor The y decay length. * @return The gradient. */ - QImage grayUnbalancedGradient(const QSize &size, unsigned char ca, + QImage grayUnbalancedGradient(QSize size, unsigned char ca, unsigned char cb, GradientType type, int xfactor=100, int yfactor=100); } #endif diff --git a/src/utils/string_utils.cpp b/src/utils/string_utils.cpp index 3eeea803..85110d47 100644 --- a/src/utils/string_utils.cpp +++ b/src/utils/string_utils.cpp @@ -1,209 +1,222 @@ /*************************************************************************** Copyright (C) 2003-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #include "string_utils.h" #include "../fieldformat.h" #include #include #include #include #include #include #include namespace { static const int STRING_STORE_SIZE = 4999; // too big, too small? } QString Tellico::decodeHTML(const QByteArray& data_) { return decodeHTML(fromHtmlData(data_)); } QString Tellico::decodeHTML(const QString& text) { return KCharsets::resolveEntities(text); } QString Tellico::uid(int l, bool prefix) { QString uid; if(prefix) { uid = QStringLiteral("Tellico"); } uid.append(KRandom::randomString(qMax(l - uid.length(), 0))); return uid; } uint Tellico::toUInt(const QString& s, bool* ok) { if(s.isEmpty()) { if(ok) { *ok = false; } return 0; } int idx = 0; while(idx < s.length() && s[idx].isDigit()) { ++idx; } if(idx == 0) { if(ok) { *ok = false; } return 0; } return s.leftRef(idx).toUInt(ok); } QString Tellico::i18nReplace(QString text) { // Because QDomDocument sticks in random newlines, go ahead and grab them too static QRegularExpression rx(QStringLiteral("(?:\\n+ *)*(.*?)(?: *\\n+)*"), QRegularExpression::OptimizeOnFirstUsageOption); QRegularExpressionMatch match = rx.match(text); while(match.hasMatch()) { // KDE bug 254863, be sure to escape just in case of spurious & entities text.replace(match.capturedStart(), match.capturedLength(), i18n(match.captured(1).toUtf8().constData()).toHtmlEscaped()); match = rx.match(text, match.capturedStart()+1); } return text; } int Tellico::stringHash(const QString& str) { uint h = 0; uint g = 0; for(int i = 0; i < str.length(); ++i) { h = (h << 4) + str.unicode()[i].cell(); if((g = h & 0xf0000000)) { h ^= g >> 24; } h &= ~g; } int index = h; return index < 0 ? -index : index; } QString Tellico::shareString(const QString& str) { static QString stringStore[STRING_STORE_SIZE]; const int hash = stringHash(str) % STRING_STORE_SIZE; if(stringStore[hash] != str) { stringStore[hash] = str; } return stringStore[hash]; } QString Tellico::minutes(int seconds) { int min = seconds / 60; seconds = seconds % 60; return QString::number(min) + QLatin1Char(':') + QString::number(seconds).rightJustified(2, QLatin1Char('0')); } QString Tellico::fromHtmlData(const QByteArray& data_, const char* codecName) { QTextCodec* codec = codecName ? QTextCodec::codecForHtml(data_, QTextCodec::codecForName(codecName)) : QTextCodec::codecForHtml(data_); return codec->toUnicode(data_); } QString Tellico::removeAccents(const QString& value_) { static QCache stringCache(STRING_STORE_SIZE); if(stringCache.contains(value_)) { return *stringCache.object(value_); } static QRegularExpression rx; if(rx.pattern().isEmpty()) { QString pattern(QStringLiteral("(?:")); for(int i = 0x0300; i <= 0x036F; ++i) { pattern += QChar(i) + QLatin1Char('|'); } pattern.chop(1); pattern += QLatin1Char(')'); rx.setPattern(pattern); rx.optimize(); } // remove accents from table "Combining Diacritical Marks" const QString value2 = value_.normalized(QString::NormalizationForm_D).remove(rx); stringCache.insert(value_, new QString(value2)); return value2; } QString Tellico::mapValue(const QVariantMap& map, const char* name) { const QVariant v = map.value(QLatin1String(name)); if(v.isNull()) { return QString(); } else if(v.canConvert(QVariant::String)) { return v.toString(); } else if(v.canConvert(QVariant::StringList)) { return v.toStringList().join(FieldFormat::delimiterString()); } else if(v.canConvert(QVariant::Map)) { // FilmasterFetcher, OpenLibraryFetcher and VNDBFetcher depend on the default "value" field return v.toMap().value(QStringLiteral("value")).toString(); } else { return QString(); } } QString Tellico::mapValue(const QVariantMap& map, const char* object, const char* name) { const QVariant v = map.value(QLatin1String(object)); if(v.isNull()) { return QString(); } else if(v.canConvert(QVariant::Map)) { return mapValue(v.toMap(), name); } else if(v.canConvert(QVariant::List)) { QStringList values; foreach(QVariant v, v.toList()) { const QString s = mapValue(v.toMap(), name); if(!s.isEmpty()) values += s; } return values.join(FieldFormat::delimiterString()); } else { return QString(); } } QByteArray Tellico::obfuscate(const QString& string) { QByteArray b; b.reserve(string.length() * 2); for(int p = 0; p < string.length(); p++) { char c = KRandom::random() % 255; b.prepend(c ^ string.at(p).unicode()); b.prepend(c); } return b.toHex(); } QString Tellico::reverseObfuscate(const QByteArray& bytes) { if(bytes.length() % 2 != 0 || bytes.isEmpty()) { return QString(); } const QByteArray b = QByteArray::fromHex(bytes); QString result; result.reserve(b.length() / 2); for(int p = b.length()-1; p >= 0; p -= 2) { result.append(QLatin1Char(b.at(p-1) ^ b.at(p))); } return result; } + +QString Tellico::removeControlCodes(const QString& string) { + QString result; + result.reserve(string.size()); + for(int i = 0; i < string.size(); ++i) { + const ushort c = string.at(i).unicode(); + // legal control codes in XML 1.0 are U+0009, U+000A, U+000D + if(c > 31 || c == 9 || c == 10 || c == 13) { + result += string.at(i); + } + } + return result; +} diff --git a/src/utils/string_utils.h b/src/utils/string_utils.h index bbcd10af..326bfa1e 100644 --- a/src/utils/string_utils.h +++ b/src/utils/string_utils.h @@ -1,75 +1,77 @@ /*************************************************************************** Copyright (C) 2003-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #ifndef STRING_UTILS_H #define STRING_UTILS_H class QByteArray; class QString; #include #include /** * This file contains utility functions for manipulating strings. * * @author Robby Stephenson */ namespace Tellico { /** * Decode HTML entities. Only numeric entities are handled currently. */ QString decodeHTML(const QByteArray& data); QString decodeHTML(const QString& text); /** * Return a random, and almost certainly unique UID. * * @param length The UID starts with "Tellico" and adds enough letters to be @p length long. */ QString uid(int length=20, bool prefix=true); uint toUInt(const QString& string, bool* ok); /** * Replace all occurrences of text with i18n("text") */ QString i18nReplace(QString text); QString removeAccents(const QString& value); int stringHash(const QString& str); /** take advantage string collisions to reduce memory */ QString shareString(const QString& str); QString minutes(int seconds); QString fromHtmlData(const QByteArray& data, const char* codecName = nullptr); // helper methods for the QVariantMaps used by the JSON importers QString mapValue(const QVariantMap& map, const char* object); QString mapValue(const QVariantMap& map, const char* object, const char* name); QByteArray obfuscate(const QString& string); QString reverseObfuscate(const QByteArray& bytes); + + QString removeControlCodes(const QString& string); } #endif