File indexing completed on 2024-04-21 04:32:04
0001 /* 0002 * Copyright (C) 2010-2015 by Stephen Allewell 0003 * steve.allewell@gmail.com 0004 * 0005 * This program is free software; you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation; either version 2 of the License, or 0008 * (at your option) any later version. 0009 */ 0010 0011 #include "DocumentPalette.h" 0012 0013 #include <KLocalizedString> 0014 #include <KMessageBox> 0015 0016 #include "Exceptions.h" 0017 #include "FlossScheme.h" 0018 #include "SchemeManager.h" 0019 #include "SymbolLibrary.h" 0020 #include "SymbolManager.h" 0021 0022 #include "configuration.h" 0023 0024 class DocumentPaletteData : public QSharedData 0025 { 0026 public: 0027 DocumentPaletteData(); 0028 DocumentPaletteData(const DocumentPaletteData &); 0029 ~DocumentPaletteData(); 0030 0031 static const int version = 103; // added m_symbolLibrary 0032 0033 QString m_schemeName; 0034 QString m_symbolLibrary; 0035 int m_currentIndex; 0036 QMap<int, DocumentFloss *> m_documentFlosses; 0037 }; 0038 0039 DocumentPaletteData::DocumentPaletteData() 0040 : QSharedData() 0041 , m_schemeName(Configuration::palette_DefaultScheme()) 0042 , m_symbolLibrary(QLatin1String("kxstitch")) 0043 , m_currentIndex(-1) 0044 { 0045 } 0046 0047 DocumentPaletteData::DocumentPaletteData(const DocumentPaletteData &other) 0048 : QSharedData(other) 0049 , m_schemeName(other.m_schemeName) 0050 , m_symbolLibrary(other.m_symbolLibrary) 0051 , m_currentIndex(other.m_currentIndex) 0052 { 0053 for (QMap<int, DocumentFloss *>::const_iterator i = other.m_documentFlosses.constBegin(); i != other.m_documentFlosses.constEnd(); ++i) { 0054 m_documentFlosses.insert(i.key(), new DocumentFloss(other.m_documentFlosses.value(i.key()))); 0055 } 0056 } 0057 0058 DocumentPaletteData::~DocumentPaletteData() 0059 { 0060 qDeleteAll(m_documentFlosses); 0061 } 0062 0063 DocumentPalette::DocumentPalette() 0064 : d(new DocumentPaletteData) 0065 { 0066 } 0067 0068 DocumentPalette::DocumentPalette(const DocumentPalette &other) 0069 : d(other.d) 0070 { 0071 } 0072 0073 DocumentPalette::~DocumentPalette() 0074 { 0075 } 0076 0077 QString DocumentPalette::schemeName() const 0078 { 0079 return d->m_schemeName; 0080 } 0081 0082 QString DocumentPalette::symbolLibrary() const 0083 { 0084 return d->m_symbolLibrary; 0085 } 0086 0087 QMap<int, DocumentFloss *> DocumentPalette::flosses() const 0088 { 0089 return d->m_documentFlosses; 0090 } 0091 0092 QVector<int> DocumentPalette::sortedFlosses() const 0093 { 0094 int colors = d->m_documentFlosses.count(); 0095 QVector<int> sorted = d->m_documentFlosses.keys().toVector(); 0096 0097 bool exchanged; 0098 0099 do { 0100 exchanged = false; 0101 0102 for (int i = 0; i < colors - 1; ++i) { 0103 QString flossName1(d->m_documentFlosses.value(sorted[i])->flossName()); 0104 QString flossName2(d->m_documentFlosses.value(sorted[i + 1])->flossName()); 0105 int length1 = flossName1.length(); 0106 int length2 = flossName2.length(); 0107 0108 if (((flossName1 > flossName2) && (length1 >= length2)) || (length1 > length2)) { 0109 int tmp = sorted.value(i); 0110 sorted[i] = sorted.value(i + 1); 0111 sorted[i + 1] = tmp; 0112 exchanged = true; 0113 } 0114 } 0115 } while (exchanged); 0116 0117 return sorted; 0118 } 0119 0120 QList<qint16> DocumentPalette::usedSymbols() const 0121 { 0122 QList<qint16> used; 0123 QList<int> keys = d->m_documentFlosses.keys(); 0124 0125 foreach (int index, keys) { 0126 used << d->m_documentFlosses[index]->stitchSymbol(); 0127 } 0128 0129 return used; 0130 } 0131 0132 const DocumentFloss *DocumentPalette::currentFloss() const 0133 { 0134 DocumentFloss *documentFloss = nullptr; 0135 0136 if (d->m_currentIndex != -1) { 0137 documentFloss = d->m_documentFlosses.value(d->m_currentIndex); 0138 } 0139 0140 return documentFloss; 0141 } 0142 0143 DocumentFloss *DocumentPalette::floss(int colorIndex) 0144 { 0145 DocumentFloss *documentFloss = nullptr; 0146 0147 if (d->m_documentFlosses.contains(colorIndex)) { 0148 documentFloss = d->m_documentFlosses.value(colorIndex); 0149 } 0150 0151 return documentFloss; 0152 } 0153 0154 int DocumentPalette::currentIndex() const 0155 { 0156 return d->m_currentIndex; 0157 } 0158 0159 void DocumentPalette::setSchemeName(const QString &schemeName) 0160 { 0161 if (d->m_schemeName == schemeName) { 0162 return; 0163 } 0164 0165 d->m_schemeName = schemeName; 0166 0167 FlossScheme *scheme = SchemeManager::scheme(d->m_schemeName); 0168 0169 for (QMap<int, DocumentFloss *>::const_iterator i = d->m_documentFlosses.constBegin(); i != d->m_documentFlosses.constEnd(); ++i) { 0170 DocumentFloss *documentFloss = i.value(); 0171 Floss *floss = scheme->convert(documentFloss->flossColor()); 0172 DocumentFloss *replacement = new DocumentFloss(floss->name(), 0173 documentFloss->stitchSymbol(), 0174 documentFloss->backstitchSymbol(), 0175 documentFloss->stitchStrands(), 0176 documentFloss->backstitchStrands()); 0177 replacement->setFlossColor(floss->color()); 0178 delete documentFloss; 0179 replace(i.key(), replacement); 0180 } 0181 } 0182 0183 void DocumentPalette::setSymbolLibrary(const QString &symbolLibrary) 0184 { 0185 QList<qint16> indexes = SymbolManager::library(symbolLibrary)->indexes(); 0186 0187 // only change the symbol library if there are enough symbols available 0188 if (d->m_documentFlosses.count() <= indexes.count()) { 0189 d->m_symbolLibrary = symbolLibrary; 0190 0191 foreach (DocumentFloss *documentFloss, d->m_documentFlosses) { 0192 documentFloss->setStitchSymbol(indexes.takeFirst()); 0193 } 0194 } 0195 } 0196 0197 void DocumentPalette::setCurrentIndex(int currentIndex) 0198 { 0199 d->m_currentIndex = currentIndex; 0200 } 0201 0202 void DocumentPalette::add(int flossIndex, DocumentFloss *documentFloss) 0203 { 0204 d->m_documentFlosses.insert(flossIndex, documentFloss); 0205 0206 if (d->m_currentIndex == -1) { 0207 d->m_currentIndex = 0; 0208 } 0209 } 0210 0211 int DocumentPalette::add(const QColor &srcColor) 0212 { 0213 int colorIndex = -1; 0214 0215 FlossScheme *scheme = SchemeManager::scheme(d->m_schemeName); 0216 Floss *floss = scheme->find(srcColor); 0217 0218 if (floss == nullptr) { 0219 floss = scheme->convert(srcColor); 0220 } 0221 0222 for (QMap<int, DocumentFloss *>::const_iterator i = d->m_documentFlosses.constBegin(); i != d->m_documentFlosses.constEnd(); ++i) { 0223 if (i.value()->flossColor() == floss->color()) { 0224 colorIndex = i.key(); 0225 } 0226 } 0227 0228 if (colorIndex == -1) { // the color hasn't been found in the existing list 0229 colorIndex = freeIndex(); 0230 DocumentFloss *documentFloss = 0231 new DocumentFloss(floss->name(), freeSymbol(), Qt::SolidLine, Configuration::palette_StitchStrands(), Configuration::palette_BackstitchStrands()); 0232 documentFloss->setFlossColor(floss->color()); 0233 add(colorIndex, documentFloss); 0234 } 0235 0236 return colorIndex; 0237 } 0238 0239 DocumentFloss *DocumentPalette::remove(int flossIndex) 0240 { 0241 DocumentFloss *documentFloss = d->m_documentFlosses.take(flossIndex); 0242 0243 if (d->m_documentFlosses.count() == 0) { 0244 d->m_currentIndex = -1; 0245 } 0246 0247 return documentFloss; 0248 } 0249 0250 DocumentFloss *DocumentPalette::replace(int flossIndex, DocumentFloss *documentFloss) 0251 { 0252 DocumentFloss *old = d->m_documentFlosses.take(flossIndex); 0253 d->m_documentFlosses.insert(flossIndex, documentFloss); 0254 return old; 0255 } 0256 0257 void DocumentPalette::swap(int originalIndex, int swappedIndex) 0258 { 0259 DocumentFloss *original = d->m_documentFlosses.take(originalIndex); 0260 d->m_documentFlosses.insert(originalIndex, d->m_documentFlosses.take(swappedIndex)); 0261 d->m_documentFlosses.insert(swappedIndex, original); 0262 } 0263 0264 DocumentPalette &DocumentPalette::operator=(const DocumentPalette &other) 0265 { 0266 d = other.d; 0267 return *this; 0268 } 0269 0270 bool DocumentPalette::operator==(const DocumentPalette &other) const 0271 { 0272 return d == other.d; 0273 } 0274 0275 bool DocumentPalette::operator!=(const DocumentPalette &other) const 0276 { 0277 return d != other.d; 0278 } 0279 0280 QDataStream &operator<<(QDataStream &stream, const DocumentPalette &documentPalette) 0281 { 0282 stream << qint32(DocumentPaletteData::version); 0283 stream << documentPalette.d->m_schemeName; 0284 stream << documentPalette.d->m_symbolLibrary; 0285 stream << qint32(documentPalette.d->m_currentIndex); 0286 stream << qint32(documentPalette.d->m_documentFlosses.count()); 0287 0288 for (QMap<int, DocumentFloss *>::const_iterator i = documentPalette.d->m_documentFlosses.constBegin(); i != documentPalette.d->m_documentFlosses.constEnd(); 0289 ++i) { 0290 stream << qint32(i.key()); 0291 stream << *i.value(); 0292 } 0293 0294 if (stream.status() != QDataStream::Ok) { 0295 throw FailedWriteFile(stream.status()); 0296 } 0297 0298 return stream; 0299 } 0300 0301 QDataStream &operator>>(QDataStream &stream, DocumentPalette &documentPalette) 0302 { 0303 qint32 version; 0304 qint32 currentIndex; 0305 qint32 documentPaletteCount; 0306 bool showSymbols; 0307 0308 qint32 key; 0309 DocumentFloss *documentFloss; 0310 0311 documentPalette = DocumentPalette(); 0312 0313 stream >> version; 0314 0315 switch (version) { 0316 case 103: 0317 stream >> documentPalette.d->m_schemeName; 0318 stream >> documentPalette.d->m_symbolLibrary; 0319 stream >> currentIndex; 0320 documentPalette.d->m_currentIndex = currentIndex; 0321 stream >> documentPaletteCount; 0322 0323 while (documentPaletteCount--) { 0324 documentFloss = new DocumentFloss; 0325 stream >> key; 0326 stream >> *documentFloss; 0327 documentPalette.d->m_documentFlosses.insert(key, documentFloss); 0328 } 0329 0330 break; 0331 0332 case 102: 0333 stream >> documentPalette.d->m_schemeName; 0334 documentPalette.d->m_symbolLibrary = QLatin1String("kxstitch"); 0335 stream >> currentIndex; 0336 documentPalette.d->m_currentIndex = currentIndex; 0337 stream >> documentPaletteCount; 0338 0339 while (documentPaletteCount--) { 0340 documentFloss = new DocumentFloss; 0341 stream >> key; 0342 stream >> *documentFloss; 0343 documentPalette.d->m_documentFlosses.insert(key, documentFloss); 0344 } 0345 0346 break; 0347 0348 case 101: 0349 stream >> documentPalette.d->m_schemeName; 0350 documentPalette.d->m_symbolLibrary = QLatin1String("kxstitch"); 0351 stream >> currentIndex; 0352 documentPalette.d->m_currentIndex = currentIndex; 0353 stream >> showSymbols; 0354 stream >> documentPaletteCount; 0355 0356 while (documentPaletteCount--) { 0357 documentFloss = new DocumentFloss; 0358 stream >> key; 0359 stream >> *documentFloss; 0360 documentPalette.d->m_documentFlosses.insert(key, documentFloss); 0361 } 0362 0363 break; 0364 0365 case 100: 0366 stream >> documentPalette.d->m_schemeName; 0367 documentPalette.d->m_symbolLibrary = QLatin1String("kxstitch"); 0368 stream >> currentIndex; 0369 documentPalette.d->m_currentIndex = currentIndex; 0370 stream >> showSymbols; 0371 stream >> documentPaletteCount; 0372 0373 while (documentPaletteCount--) { 0374 documentFloss = new DocumentFloss; 0375 stream >> key; 0376 stream >> *documentFloss; 0377 documentFloss->setStitchSymbol(documentPalette.freeSymbol()); 0378 documentPalette.d->m_documentFlosses.insert(key, documentFloss); 0379 } 0380 0381 break; 0382 0383 default: 0384 throw InvalidFileVersion(QString(i18n("Palette version %1", version))); 0385 break; 0386 } 0387 0388 if (stream.status() != QDataStream::Ok) { 0389 throw FailedReadFile(QString(i18n("Failed reading palette"))); 0390 } 0391 0392 // test DocumentFloss symbol indexes exist in the library 0393 QList<qint16> indexes = SymbolManager::library(documentPalette.symbolLibrary())->indexes(); 0394 QList<DocumentFloss *> missingSymbols; 0395 QList<int> keys = documentPalette.d->m_documentFlosses.keys(); 0396 0397 foreach (int key, keys) { 0398 documentFloss = documentPalette.d->m_documentFlosses.value(key); 0399 qint16 symbol = documentFloss->stitchSymbol(); 0400 0401 if (indexes.contains(symbol)) { 0402 indexes.removeOne(symbol); 0403 } else { 0404 missingSymbols.append(documentFloss); 0405 } 0406 } 0407 0408 // missingSymbols will contain pointers to DocumentFloss where the symbol index is not in the symbol library 0409 // check there is a sufficient quantity of symbols to allocate to the remaining flosses 0410 if (missingSymbols.count() > indexes.count()) { 0411 if (KMessageBox::Cancel 0412 == KMessageBox::warningContinueCancel( 0413 nullptr, 0414 QString(i18n("There are insufficient symbols available in the symbol library for this pattern. An extra %1 are required.", 0415 missingSymbols.count() - indexes.count())))) { 0416 throw FailedReadFile(QString(i18n("Canceled: Insufficient symbols available"))); 0417 } 0418 } 0419 0420 // iterate the list and allocate a free symbol to the missing ones. 0421 // if there is insufficient symbols to allocate, empty symbols will be assigned. 0422 QList<QString> emptySymbols; 0423 0424 foreach (DocumentFloss *const documentFloss, missingSymbols) { 0425 documentFloss->setStitchSymbol(documentPalette.freeSymbol()); 0426 if (documentFloss->stitchSymbol() == -1) { 0427 emptySymbols.append(documentFloss->flossName()); 0428 } 0429 } 0430 0431 if (int count = missingSymbols.count()) { 0432 // display an information box to show symbols have been allocated 0433 QString information(i18np("The following floss color has had its symbol\nreplaced because it did not exist in the symbol library.\n\n", 0434 "The following floss colors have had their symbols\nreplaced because they did not exist in the symbol library.\n\n", 0435 count)); 0436 0437 foreach (DocumentFloss *documentFloss, missingSymbols) { 0438 information += QString::fromLatin1("%1\n").arg(documentFloss->flossName()); 0439 } 0440 0441 if (int count = emptySymbols.count()) { 0442 information += QString(i18np("The following floss color has had an empty symbol assigned.\n\n", 0443 "The following floss colors have had an empty symbol assigned.\n\n", 0444 count)); 0445 0446 foreach (const QString &name, emptySymbols) { 0447 information += QString::fromLatin1("%1\n").arg(name); 0448 } 0449 } 0450 0451 information += QString(i18np("\nYou may want to check this is suitable.", "\nYou may want to check these are suitable.", count)); 0452 0453 KMessageBox::information(nullptr, information); 0454 } 0455 0456 return stream; 0457 } 0458 0459 int DocumentPalette::freeIndex() const 0460 { 0461 int i = 0; 0462 0463 while (d->m_documentFlosses.contains(i)) { 0464 ++i; 0465 } 0466 0467 return i; 0468 } 0469 0470 qint16 DocumentPalette::freeSymbol() const 0471 { 0472 QList<qint16> indexes = SymbolManager::library(d->m_symbolLibrary)->indexes(); 0473 QList<int> keys = d->m_documentFlosses.keys(); 0474 0475 foreach (int index, keys) { 0476 indexes.removeOne(d->m_documentFlosses[index]->stitchSymbol()); 0477 } 0478 0479 return (indexes.isEmpty() ? -1 : indexes.first()); 0480 }