File indexing completed on 2024-04-28 16:32:00

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 "utils/string_utils.h"
0027 #include "tellico_debug.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() {
0107 }
0108 
0109 void Field::setTitle(const QString& title_) {
0110   m_title = title_;
0111   if(isSingleCategory()) {
0112     m_category = title_;
0113   }
0114 }
0115 
0116 void Field::setType(Field::Type type_) {
0117   m_type = type_;
0118   if(m_type != Field::Choice) {
0119     m_allowed = QStringList();
0120   }
0121   switch(m_type) {
0122     case Table:
0123     case Table2:
0124       m_flags |= AllowMultiple;
0125       if(m_type == Table2) {
0126         m_type = Table;
0127         setProperty(QStringLiteral("columns"), QStringLiteral("2"));
0128       }
0129       if(property(QStringLiteral("columns")).isEmpty()) {
0130         setProperty(QStringLiteral("columns"), QStringLiteral("1"));
0131       }
0132       break;
0133     case Date:
0134       m_formatType = FieldFormat::FormatDate;
0135       break;
0136     case ReadOnly:
0137       m_flags |= NoEdit;
0138       m_type = Line;
0139       break;
0140     case Dependent:
0141       m_flags |= Derived;
0142       m_type = Line;
0143       break;
0144     default: // ssshhhhhhhh
0145       break;
0146   }
0147   if(isSingleCategory()) {
0148     m_category = m_title;
0149   }
0150 }
0151 
0152 void Field::setCategory(const QString& category_) {
0153   if(!isSingleCategory()) {
0154     m_category = category_;
0155   }
0156 }
0157 
0158 void Field::setFlags(int flags_) {
0159   // tables always have multiple allowed
0160   if(m_type == Table) {
0161     m_flags = AllowMultiple | flags_;
0162   } else {
0163     m_flags = flags_;
0164   }
0165 }
0166 
0167 bool Field::hasFlag(FieldFlag flag_) const {
0168   return m_flags & flag_;
0169 }
0170 
0171 void Field::setFormatType(FieldFormat::Type type_) {
0172   // Choice and Data fields are not allowed a format type
0173   if(m_type != Choice && m_type != Date) {
0174     m_formatType = type_;
0175   }
0176 }
0177 
0178 QString Field::defaultValue() const {
0179   return property(QStringLiteral("default"));
0180 }
0181 
0182 void Field::setDefaultValue(const QString& value_) {
0183   if(value_.isEmpty() || m_type != Choice || m_allowed.contains(value_)) {
0184     setProperty(QStringLiteral("default"), value_);
0185   }
0186 }
0187 
0188 bool Field::isSingleCategory() const {
0189   return (m_type == Para || m_type == Table || m_type == Image);
0190 }
0191 
0192 // if these are changed, then CollectionFieldsDialog should be checked since it
0193 // checks for equality against some of these strings
0194 Field::FieldMap Field::typeMap() {
0195   FieldMap map;
0196   map[Field::Line]      = i18n("Simple Text");
0197   map[Field::Para]      = i18n("Paragraph");
0198   map[Field::Choice]    = i18n("Choice");
0199   map[Field::Bool]      = i18n("Checkbox");
0200   map[Field::Number]    = i18n("Number");
0201   map[Field::URL]       = i18n("URL");
0202   map[Field::Table]     = i18n("Table");
0203   map[Field::Image]     = i18n("Image");
0204   map[Field::Date]      = i18n("Date");
0205   map[Field::Rating]    = i18n("Rating");
0206   return map;
0207 }
0208 
0209 // just for formatting's sake
0210 QStringList Field::typeTitles() {
0211   const FieldMap& map = typeMap();
0212   QStringList list;
0213   list.append(map[Field::Line]);
0214   list.append(map[Field::Para]);
0215   list.append(map[Field::Choice]);
0216   list.append(map[Field::Bool]);
0217   list.append(map[Field::Number]);
0218   list.append(map[Field::URL]);
0219   list.append(map[Field::Date]);
0220   list.append(map[Field::Table]);
0221   list.append(map[Field::Image]);
0222   list.append(map[Field::Rating]);
0223   return list;
0224 }
0225 
0226 void Field::addAllowed(const QString& value_) {
0227   if(m_type != Choice) {
0228     return;
0229   }
0230   if(!m_allowed.contains(value_)) {
0231     m_allowed += value_;
0232   }
0233 }
0234 
0235 void Field::setProperty(const QString& key_, const QString& value_) {
0236   if(value_.isEmpty()) {
0237     m_properties.remove(key_);
0238   } else {
0239     m_properties.insert(key_, value_);
0240   }
0241 }
0242 
0243 void Field::setPropertyList(const Tellico::StringMap& props_) {
0244   m_properties = props_;
0245 }
0246 
0247 QString Field::property(const QString& key_) const {
0248   return m_properties.contains(key_) ? m_properties.value(key_) : QString();
0249 }
0250 
0251 void Field::convertOldRating(Tellico::Data::FieldPtr field_) {
0252   if(field_->type() != Data::Field::Choice) {
0253     return; // nothing to do
0254   }
0255 
0256   if(field_->name() != QLatin1String("rating")
0257      && field_->property(QStringLiteral("rating")) != QLatin1String("true")) {
0258     return; // nothing to do
0259   }
0260 
0261   int min = 10;
0262   int max = 1;
0263   bool ok;
0264   const QStringList& allow = field_->allowed();
0265   for(QStringList::ConstIterator it = allow.begin(); it != allow.end(); ++it) {
0266     int n = Tellico::toUInt(*it, &ok);
0267     if(!ok) {
0268       return; // no need to convert
0269     }
0270     min = qMin(min, n);
0271     max = qMax(max, n);
0272   }
0273   min = qMax(max, 1);
0274   max = qMin(max, 10);
0275   if(min >= max) {
0276     min = 1;
0277     max = 5;
0278   }
0279   field_->setProperty(QStringLiteral("minimum"), QString::number(min));
0280   field_->setProperty(QStringLiteral("maximum"), QString::number(max));
0281   //remove any old property
0282   field_->setProperty(QStringLiteral("rating"), QString());
0283   field_->setType(Rating);
0284 }
0285 
0286 Tellico::Data::FieldPtr Field::createDefaultField(DefaultField fieldEnum) {
0287   Data::FieldPtr field;
0288   switch(fieldEnum) {
0289     case IDField:
0290       field = new Field(QStringLiteral("id"), i18nc("ID # of the entry", "ID"), Field::Number);
0291       field->setCategory(i18n("Personal"));
0292       field->setProperty(QStringLiteral("template"), QStringLiteral("%{@id}"));
0293       field->setFlags(Field::Derived);
0294       field->setFormatType(FieldFormat::FormatNone);
0295       break;
0296     case TitleField:
0297       field = new Field(QStringLiteral("title"), i18n("Title"));
0298       field->setCategory(i18n("General"));
0299       field->setFlags(Field::NoDelete);
0300       field->setFormatType(FieldFormat::FormatTitle);
0301       break;
0302     case CreatedDateField:
0303       field = new Field(QStringLiteral("cdate"), i18n("Date Created"), Field::Date);
0304       field->setCategory(i18n("Personal"));
0305       field->setFlags(Field::NoEdit);
0306       break;
0307     case ModifiedDateField:
0308       field = new Field(QStringLiteral("mdate"), i18n("Date Modified"), Field::Date);
0309       field->setCategory(i18n("Personal"));
0310       field->setFlags(Field::NoEdit);
0311       break;
0312     case IsbnField:
0313       field = new Field(QStringLiteral("isbn"), i18n("ISBN#"));
0314       field->setCategory(i18n("Publishing"));
0315       field->setDescription(i18n("International Standard Book Number"));
0316       break;
0317     case PegiField:
0318       {
0319       QStringList pegi = QStringLiteral("PEGI 3, PEGI 7, PEGI 12, PEGI 16, PEGI 18")
0320 #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
0321                          .split(QRegularExpression(QStringLiteral("\\s*,\\s*")), QString::SkipEmptyParts);
0322 #else
0323                          .split(QRegularExpression(QStringLiteral("\\s*,\\s*")), Qt::SkipEmptyParts);
0324 #endif
0325       field = new Field(QStringLiteral("pegi"), i18n("PEGI Rating"), pegi);
0326       }
0327       field->setCategory(i18n("General"));
0328       field->setFlags(Field::AllowGrouped);
0329       break;
0330     case ImdbField:
0331       field = new Field(QStringLiteral("imdb"), i18n("IMDb Link"), Field::URL);
0332       field->setCategory(i18n("General"));
0333       break;
0334     case EpisodeField:
0335       field = new Data::Field(QStringLiteral("episode"), i18n("Episodes"), Data::Field::Table);
0336       field->setFormatType(FieldFormat::FormatTitle);
0337       field->setProperty(QStringLiteral("columns"), QStringLiteral("3"));
0338       field->setProperty(QStringLiteral("column1"), i18n("Title"));
0339       field->setProperty(QStringLiteral("column2"), i18nc("TV Season", "Season"));
0340       field->setProperty(QStringLiteral("column3"), i18nc("TV Episode", "Episode"));
0341       break;
0342   }
0343   Q_ASSERT(field);
0344   return field;
0345 }
0346 
0347 Tellico::Data::FieldList Tellico::listIntersection(const Tellico::Data::FieldList& list1, const Tellico::Data::FieldList& list2) {
0348 #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
0349   // QList::toSet is deprecated
0350   return list1.toSet().intersect(list2.toSet()).values();
0351 #else
0352   // std::set_intersection requires sorted lists. Just do the set intersection manually
0353   Data::FieldList returnList;
0354   QSet<Tellico::Data::FieldPtr> set(list1.begin(), list1.end());
0355 
0356   std::for_each(list2.begin(), list2.end(),
0357         [&set, &returnList](const Data::FieldPtr& f) {
0358           if(set.contains(f)) {
0359             returnList.append(f);
0360             set.remove(f);
0361           }
0362         }
0363       );
0364   return returnList;
0365 #endif
0366 }