Warning, file /graphics/kxstitch/src/SchemeManager.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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 "SchemeManager.h"
0012 
0013 #include <QDir>
0014 #include <QDirIterator>
0015 #include <QFile>
0016 #include <QFileInfo>
0017 #include <QStandardPaths>
0018 #include <QUrl>
0019 #include <QXmlInputSource>
0020 #include <QXmlSimpleReader>
0021 
0022 #include <KLocalizedString>
0023 #include <KMessageBox>
0024 
0025 #include "Floss.h"
0026 #include "FlossScheme.h"
0027 #include "SchemeParser.h"
0028 
0029 SchemeManager *SchemeManager::schemeManager = nullptr;
0030 
0031 /**
0032     Accessor for the static object
0033     */
0034 SchemeManager &SchemeManager::self()
0035 {
0036     if (schemeManager == nullptr) {
0037         schemeManager = new SchemeManager();
0038     }
0039 
0040     return *schemeManager;
0041 }
0042 
0043 /**
0044     Constructor.
0045     */
0046 SchemeManager::SchemeManager()
0047     : QObject()
0048 {
0049     /** Refresh the list of floss schemes. */
0050     refresh();
0051 }
0052 
0053 /**
0054     Destructor. Delete all the floss schemes.
0055     */
0056 SchemeManager::~SchemeManager()
0057 {
0058     qDeleteAll(m_flossSchemes);
0059 }
0060 
0061 /**
0062     Create a new scheme.  The name is checked to see if it already exists, if not a new instance of a FlossScheme
0063     is created.
0064     @param schemeName the name of the scheme to be created.
0065     @return a pointer to the new FlossScheme instance, null if it already exists or a new one could not be created.
0066     */
0067 FlossScheme *SchemeManager::createScheme(QString schemeName)
0068 {
0069     FlossScheme *flossScheme = nullptr;
0070 
0071     if (scheme(schemeName) == nullptr) {
0072         if ((flossScheme = new FlossScheme)) {
0073             flossScheme->setSchemeName(schemeName);
0074             self().m_flossSchemes.append(flossScheme);
0075         }
0076     }
0077 
0078     return flossScheme;
0079 }
0080 
0081 /**
0082     Get a list of the FlossSchemes available.
0083     @return QStringList of scheme names.
0084     */
0085 QStringList SchemeManager::schemes()
0086 {
0087     QStringList schemeNames;
0088 
0089     QListIterator<FlossScheme *> i(self().m_flossSchemes);
0090 
0091     while (i.hasNext()) {
0092         schemeNames.append(i.next()->schemeName());
0093     }
0094 
0095     return schemeNames;
0096 }
0097 
0098 /**
0099     Get a pointer to a scheme by name
0100     @param name of the scheme required.
0101     @return pointer to the FlossScheme instance, returns null if no scheme found.
0102     */
0103 FlossScheme *SchemeManager::scheme(QString name)
0104 {
0105     FlossScheme *flossScheme;
0106 
0107     QListIterator<FlossScheme *> i(self().m_flossSchemes);
0108 
0109     while (i.hasNext()) {
0110         flossScheme = i.next();
0111 
0112         if (flossScheme->schemeName() == name) {
0113             return flossScheme;
0114         }
0115     }
0116 
0117     return nullptr;
0118 }
0119 
0120 /**
0121     Read a scheme.
0122     @param name path to the xml file to be read.
0123     @return pointer to the FlossScheme instance created.
0124     */
0125 FlossScheme *SchemeManager::readScheme(QString name)
0126 {
0127     SchemeParser handler;
0128     QFile xmlFile(name);
0129     QXmlInputSource source(&xmlFile);
0130     QXmlSimpleReader reader;
0131     reader.setContentHandler(&handler);
0132 
0133     bool success = reader.parse(source);
0134 
0135     if (!success) {
0136         KMessageBox::error(nullptr, i18n("Error reading scheme %1\n%2.", name, handler.errorString()), i18n("Error reading floss scheme."));
0137     }
0138 
0139     FlossScheme *flossScheme = handler.flossScheme();
0140 
0141     if (!success) {
0142         delete flossScheme;
0143         flossScheme = nullptr;
0144     } else {
0145         flossScheme->setPath(name);
0146     }
0147 
0148     return flossScheme;
0149 }
0150 
0151 /**
0152     Save a modified scheme to a writable location.
0153     @param name of the scheme to be saved.
0154     @return true if the scheme was successfully saved, false otherwise.
0155     */
0156 bool SchemeManager::writeScheme(QString name)
0157 {
0158     FlossScheme *flossScheme = scheme(name);
0159     QFileInfo fileInfo(flossScheme->path());
0160 
0161     if (!fileInfo.isWritable()) {
0162         QString writableDir = QStandardPaths::writableLocation(QStandardPaths::DataLocation); // this may be empty or may not exist
0163 
0164         if (writableDir.isEmpty()) {
0165             KMessageBox::error(nullptr, i18n("Unable to locate a writable directory\nto store the scheme."));
0166             return false;
0167             // TODO Allow user to select a location to store the calibrated schemes
0168         }
0169 
0170         writableDir += QLatin1String("/schemes/");
0171 
0172         if (!QDir(writableDir).exists()) {
0173             if (!QDir().mkpath(writableDir)) {
0174                 KMessageBox::error(nullptr, i18n("Unable to locate a writable directory\nto store the scheme."));
0175                 return false;
0176                 // TODO Allow user to select a location to store the calibrated schemes
0177             }
0178         }
0179 
0180         // at this point a writable directory should be in writableDir
0181 
0182         QString writablePath = writableDir + fileInfo.fileName();
0183         fileInfo.setFile(writablePath);
0184     }
0185 
0186     QFile schemeFile(fileInfo.filePath());
0187 
0188     if (schemeFile.open(QIODevice::WriteOnly)) {
0189         QTextStream stream(&schemeFile);
0190         stream << "<!DOCTYPE flossScheme SYSTEM \"flossScheme.dtd\">\n<flossScheme>\n";
0191         stream << "<title>" << name.replace(QLatin1Char('&'), QLatin1String("&amp;")) << "</title>\n"; // includes fixup for the J&P Coates scheme
0192         QListIterator<Floss *> it(flossScheme->flosses());
0193 
0194         while (it.hasNext()) {
0195             Floss *floss = it.next();
0196             stream << "<floss><name>" << floss->name() << "</name><description>" << floss->description() << "</description>";
0197             stream << "<color><red>" << floss->color().red() << "</red><green>" << floss->color().green() << "</green><blue>" << floss->color().blue()
0198                    << "</blue></color></floss>\n";
0199         }
0200 
0201         stream << "</flossScheme>\n";
0202         schemeFile.close();
0203 
0204         return true;
0205     }
0206 
0207     return false;
0208 }
0209 
0210 /**
0211     Get a list of files that contain xml schemes, iterating each one to create a new FlossScheme instance
0212     and read the scheme.  Each path found is added to the KDirWatch instance to allow automatic reload of scheme
0213     data if it is changed outside of KXStitch.
0214     Assumes that local resources are given before global ones and should take priority.
0215     */
0216 void SchemeManager::refresh()
0217 {
0218     const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::DataLocation, QStringLiteral("schemes"), QStandardPaths::LocateDirectory);
0219 
0220     Q_FOREACH (const QString &dir, dirs) {
0221         QDirIterator it(dir, QStringList() << QLatin1String("*.xml"));
0222 
0223         while (it.hasNext()) {
0224             FlossScheme *flossScheme = readScheme(it.next());
0225 
0226             if (flossScheme) {
0227                 for (int i = 0; i < m_flossSchemes.count(); ++i) {
0228                     if (m_flossSchemes.at(i)->schemeName() == flossScheme->schemeName()) {
0229                         delete flossScheme;
0230                         flossScheme = nullptr;
0231                         break;
0232                     }
0233                 }
0234 
0235                 if (flossScheme) {
0236                     m_flossSchemes.append(flossScheme);
0237                 }
0238             }
0239         }
0240     }
0241 }
0242 
0243 #include "moc_SchemeManager.cpp"