File indexing completed on 2024-04-28 05:08:22

0001 /***************************************************************************
0002     Copyright (C) 2001-2009 Robby Stephenson <robby@periapsis.org>
0003  ***************************************************************************/
0004 
0005 /***************************************************************************
0006  *                                                                         *
0007  *   This program is free software; you can redistribute it and/or         *
0008  *   modify it under the terms of the GNU General Public License as        *
0009  *   published by the Free Software Foundation; either version 2 of        *
0010  *   the License or (at your option) version 3 or any later version        *
0011  *   accepted by the membership of KDE e.V. (or its successor approved     *
0012  *   by the membership of KDE e.V.), which shall act as a proxy            *
0013  *   defined in Section 14 of version 3 of the license.                    *
0014  *                                                                         *
0015  *   This program is distributed in the hope that it will be useful,       *
0016  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0017  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0018  *   GNU General Public License for more details.                          *
0019  *                                                                         *
0020  *   You should have received a copy of the GNU General Public License     *
0021  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
0022  *                                                                         *
0023  ***************************************************************************/
0024 
0025 #include "field.h"
0026 #include "fieldformat.h"
0027 #include "utils/string_utils.h"
0028 
0029 #include <KLocalizedString>
0030 
0031 #include <algorithm>
0032 
0033 using namespace Tellico;
0034 using Tellico::Data::Field;
0035 
0036 // this constructor is for anything but Choice type
0037 Field::Field(const QString& name_, const QString& title_, Type type_/*=Line*/)
0038     : QSharedData(), m_name(Tellico::shareString(name_)), m_title(title_),  m_category(i18n("General")), m_desc(title_),
0039       m_type(type_), m_flags(0), m_formatType(FieldFormat::FormatNone) {
0040 
0041   Q_ASSERT(m_type != Choice);
0042   // a paragraph's category is always its title, along with tables
0043   if(isSingleCategory()) {
0044     m_category = m_title;
0045   }
0046   switch(m_type) {
0047     case Table:
0048     case Table2:
0049       m_flags = AllowMultiple;
0050       if(m_type == Table2) {
0051         m_type = Table;
0052         setProperty(QStringLiteral("columns"), QStringLiteral("2"));
0053       } else {
0054         setProperty(QStringLiteral("columns"), QStringLiteral("1"));
0055       }
0056       break;
0057     case Date:  // hidden from user
0058       m_formatType = FieldFormat::FormatDate;
0059       break;
0060     case Rating:
0061       setProperty(QStringLiteral("minimum"), QStringLiteral("1"));
0062       setProperty(QStringLiteral("maximum"), QStringLiteral("5"));
0063       break;
0064     case ReadOnly:
0065       m_flags = NoEdit;
0066       m_type = Line;
0067       break;
0068     case Dependent:
0069       m_flags = Derived;
0070       m_type = Line;
0071       break;
0072     default: // ssshhhhhhhh
0073       break;
0074   }
0075 }
0076 
0077 // if this constructor is called, the type is necessarily Choice
0078 Field::Field(const QString& name_, const QString& title_, const QStringList& allowed_)
0079     : QSharedData(), m_name(Tellico::shareString(name_)), m_title(title_), m_category(i18n("General")), m_desc(title_),
0080       m_type(Field::Choice), m_allowed(allowed_), m_flags(0), m_formatType(FieldFormat::FormatNone) {
0081 }
0082 
0083 Field::Field(const Field& field_)
0084     : QSharedData(field_), m_name(field_.name()), m_title(field_.title()), m_category(field_.category()),
0085       m_desc(field_.description()), m_type(field_.type()), m_allowed(field_.allowed()),
0086       m_flags(field_.flags()), m_formatType(field_.formatType()),
0087       m_properties(field_.propertyList()) {
0088 }
0089 
0090 Field& Field::operator=(const Field& field_) {
0091   if(this == &field_) return *this;
0092 
0093 //  static_cast<QSharedData&>(*this) = static_cast<const QSharedData&>(field_);
0094   m_name = field_.name();
0095   m_title = field_.title();
0096   m_category = field_.category();
0097   m_desc = field_.description();
0098   m_type = field_.type();
0099   m_allowed = field_.allowed();
0100   m_flags = field_.flags();
0101   m_formatType = field_.formatType();
0102   m_properties = field_.propertyList();
0103   return *this;
0104 }
0105 
0106 Field::~Field() = default;
0107 
0108 void Field::setTitle(const QString& title_) {
0109   m_title = title_;
0110   if(isSingleCategory()) {
0111     m_category = title_;
0112   }
0113 }
0114 
0115 void Field::setType(Field::Type type_) {
0116   m_type = type_;
0117   if(m_type != Field::Choice) {
0118     m_allowed = QStringList();
0119   }
0120   switch(m_type) {
0121     case Table:
0122     case Table2:
0123       m_flags |= AllowMultiple;
0124       if(m_type == Table2) {
0125         m_type = Table;
0126         setProperty(QStringLiteral("columns"), QStringLiteral("2"));
0127       }
0128       if(property(QStringLiteral("columns")).isEmpty()) {
0129         setProperty(QStringLiteral("columns"), QStringLiteral("1"));
0130       }
0131       break;
0132     case Date:
0133       m_formatType = FieldFormat::FormatDate;
0134       break;
0135     case ReadOnly:
0136       m_flags |= NoEdit;
0137       m_type = Line;
0138       break;
0139     case Dependent:
0140       m_flags |= Derived;
0141       m_type = Line;
0142       break;
0143     default: // ssshhhhhhhh
0144       break;
0145   }
0146   if(isSingleCategory()) {
0147     m_category = m_title;
0148   }
0149 }
0150 
0151 void Field::setCategory(const QString& category_) {
0152   if(!isSingleCategory()) {
0153     m_category = category_;
0154   }
0155 }
0156 
0157 void Field::setFlags(int flags_) {
0158   // tables always have multiple allowed
0159   if(m_type == Table) {
0160     m_flags = AllowMultiple | flags_;
0161   } else {
0162     m_flags = flags_;
0163   }
0164 }
0165 
0166 bool Field::hasFlag(FieldFlag flag_) const {
0167   return m_flags & flag_;
0168 }
0169 
0170 void Field::setFormatType(FieldFormat::Type type_) {
0171   // Choice and Data fields are not allowed a format type
0172   if(m_type != Choice && m_type != Date) {
0173     m_formatType = type_;
0174   }
0175 }
0176 
0177 QString Field::defaultValue() const {
0178   return property(QStringLiteral("default"));
0179 }
0180 
0181 void Field::setDefaultValue(const QString& value_) {
0182   if(value_.isEmpty() || m_type != Choice || m_allowed.contains(value_)) {
0183     setProperty(QStringLiteral("default"), value_);
0184   }
0185 }
0186 
0187 bool Field::isSingleCategory() const {
0188   return (m_type == Para || m_type == Table || m_type == Image);
0189 }
0190 
0191 // if these are changed, then CollectionFieldsDialog should be checked since it
0192 // checks for equality against some of these strings
0193 Field::FieldMap Field::typeMap() {
0194   FieldMap map;
0195   map[Field::Line]      = i18n("Simple Text");
0196   map[Field::Para]      = i18n("Paragraph");
0197   map[Field::Choice]    = i18n("Choice");
0198   map[Field::Bool]      = i18n("Checkbox");
0199   map[Field::Number]    = i18n("Number");
0200   map[Field::URL]       = i18n("URL");
0201   map[Field::Table]     = i18n("Table");
0202   map[Field::Image]     = i18n("Image");
0203   map[Field::Date]      = i18n("Date");
0204   map[Field::Rating]    = i18n("Rating");
0205   return map;
0206 }
0207 
0208 // just for formatting's sake
0209 QStringList Field::typeTitles() {
0210   const FieldMap& map = typeMap();
0211   QStringList list;
0212   list.append(map[Field::Line]);
0213   list.append(map[Field::Para]);
0214   list.append(map[Field::Choice]);
0215   list.append(map[Field::Bool]);
0216   list.append(map[Field::Number]);
0217   list.append(map[Field::URL]);
0218   list.append(map[Field::Date]);
0219   list.append(map[Field::Table]);
0220   list.append(map[Field::Image]);
0221   list.append(map[Field::Rating]);
0222   return list;
0223 }
0224 
0225 void Field::addAllowed(const QString& value_) {
0226   if(m_type != Choice) {
0227     return;
0228   }
0229   if(!m_allowed.contains(value_)) {
0230     m_allowed += value_;
0231   }
0232 }
0233 
0234 void Field::setProperty(const QString& key_, const QString& value_) {
0235   if(value_.isEmpty()) {
0236     m_properties.remove(key_);
0237   } else {
0238     m_properties.insert(key_, value_);
0239   }
0240 }
0241 
0242 void Field::setPropertyList(const Tellico::StringMap& props_) {
0243   m_properties = props_;
0244 }
0245 
0246 QString Field::property(const QString& key_) const {
0247   return m_properties.value(key_);
0248 }
0249 
0250 void Field::convertOldRating(Tellico::Data::FieldPtr field_) {
0251   if(field_->type() != Data::Field::Choice) {
0252     return; // nothing to do
0253   }
0254 
0255   if(field_->name() != QLatin1String("rating")
0256      && field_->property(QStringLiteral("rating")) != QLatin1String("true")) {
0257     return; // nothing to do
0258   }
0259 
0260   int min = 10;
0261   int max = 1;
0262   bool ok;
0263   const QStringList& allow = field_->allowed();
0264   for(QStringList::ConstIterator it = allow.begin(); it != allow.end(); ++it) {
0265     int n = Tellico::toUInt(*it, &ok);
0266     if(!ok) {
0267       return; // no need to convert
0268     }
0269     min = qMin(min, n);
0270     max = qMax(max, n);
0271   }
0272   min = qMax(max, 1);
0273   max = qMin(max, 10);
0274   if(min >= max) {
0275     min = 1;
0276     max = 5;
0277   }
0278   field_->setProperty(QStringLiteral("minimum"), QString::number(min));
0279   field_->setProperty(QStringLiteral("maximum"), QString::number(max));
0280   //remove any old property
0281   field_->setProperty(QStringLiteral("rating"), QString());
0282   field_->setType(Rating);
0283 }
0284 
0285 Tellico::Data::FieldPtr Field::createDefaultField(DefaultField fieldEnum) {
0286   Data::FieldPtr field;
0287   switch(fieldEnum) {
0288     case IDField:
0289       field = new Field(QStringLiteral("id"), i18nc("ID # of the entry", "ID"), Field::Number);
0290       field->setCategory(i18n("Personal"));
0291       field->setProperty(QStringLiteral("template"), QStringLiteral("%{@id}"));
0292       field->setFlags(Field::Derived);
0293       field->setFormatType(FieldFormat::FormatNone);
0294       break;
0295     case TitleField:
0296       field = new Field(QStringLiteral("title"), i18n("Title"));
0297       field->setCategory(i18n("General"));
0298       field->setFlags(Field::NoDelete);
0299       field->setFormatType(FieldFormat::FormatTitle);
0300       break;
0301     case CreatedDateField:
0302       field = new Field(QStringLiteral("cdate"), i18n("Date Created"), Field::Date);
0303       field->setCategory(i18n("Personal"));
0304       field->setFlags(Field::NoEdit);
0305       break;
0306     case ModifiedDateField:
0307       field = new Field(QStringLiteral("mdate"), i18n("Date Modified"), Field::Date);
0308       field->setCategory(i18n("Personal"));
0309       field->setFlags(Field::NoEdit);
0310       break;
0311     case IsbnField:
0312       field = new Field(QStringLiteral("isbn"), i18n("ISBN#"));
0313       field->setCategory(i18n("Publishing"));
0314       field->setDescription(i18n("International Standard Book Number"));
0315       break;
0316     case LccnField:
0317       field = new Field(QStringLiteral("lccn"), i18n("LCCN#"));
0318       field->setCategory(i18n("Publishing"));
0319       field->setDescription(i18n("Library of Congress Control Number"));
0320       break;
0321     case PegiField:
0322       {
0323       QStringList pegi = QStringLiteral("PEGI 3, PEGI 7, PEGI 12, PEGI 16, PEGI 18")
0324 #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
0325                          .split(FieldFormat::commaSplitRegularExpression(), QString::SkipEmptyParts);
0326 #else
0327                          .split(FieldFormat::commaSplitRegularExpression(), Qt::SkipEmptyParts);
0328 #endif
0329       field = new Field(QStringLiteral("pegi"), i18n("PEGI Rating"), pegi);
0330       }
0331       field->setCategory(i18n("General"));
0332       field->setFlags(Field::AllowGrouped);
0333       break;
0334     case ImdbField:
0335       field = new Field(QStringLiteral("imdb"), i18n("IMDb Link"), Field::URL);
0336       field->setCategory(i18n("General"));
0337       break;
0338     case EpisodeField:
0339       field = new Data::Field(QStringLiteral("episode"), i18n("Episodes"), Data::Field::Table);
0340       field->setFormatType(FieldFormat::FormatTitle);
0341       field->setProperty(QStringLiteral("columns"), QStringLiteral("3"));
0342       field->setProperty(QStringLiteral("column1"), i18n("Title"));
0343       field->setProperty(QStringLiteral("column2"), i18nc("TV Season", "Season"));
0344       field->setProperty(QStringLiteral("column3"), i18nc("TV Episode", "Episode"));
0345       break;
0346     case ScreenshotField:
0347       field = new Field(QStringLiteral("screenshot"), i18n("Screenshot"), Field::Image);
0348       break;
0349     case FrontCoverField:
0350       field = new Field(QStringLiteral("cover"), i18n("Front Cover"), Field::Image);
0351       break;      
0352   }
0353   Q_ASSERT(field);
0354   return field;
0355 }
0356 
0357 Tellico::Data::FieldList Tellico::listIntersection(const Tellico::Data::FieldList& list1, const Tellico::Data::FieldList& list2) {
0358 #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
0359   // QList::toSet is deprecated
0360   return list1.toSet().intersect(list2.toSet()).values();
0361 #else
0362   // std::set_intersection requires sorted lists. Just do the set intersection manually
0363   Data::FieldList returnList;
0364   QSet<Tellico::Data::FieldPtr> set(list1.begin(), list1.end());
0365 
0366   std::for_each(list2.begin(), list2.end(),
0367         [&set, &returnList](const Data::FieldPtr& f) {
0368           if(set.contains(f)) {
0369             returnList.append(f);
0370             set.remove(f);
0371           }
0372         }
0373       );
0374   return returnList;
0375 #endif
0376 }