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 }