1 /*
2  * Copyright (C) 2010-2015 by Stephen Allewell
3  * steve.allewell@gmail.com
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  */
10 
11 
12 /**
13  * @file
14  * Implement the SymbolManager class. This loads all symbol libraries from the kxstitch application
15  * data folders and allows the selection of a library by name. The manager is implemented as a
16  * singleton class accessible from anywhere in the application. Symbols need to be available to the
17  * palette manager and from the renderer.
18  */
19 
20 
21 /**
22  * @page symbol_manager Symbol Manager
23  * The symbol manager provides an interface between the application and the collection of symbol sets.
24  * A number of symbol libraries can exist and a particular set can be selected in the palette manager
25  * symbol selector.
26  */
27 
28 
29 #include "SymbolManager.h"
30 
31 #include <QDir>
32 #include <QDirIterator>
33 #include <QFile>
34 #include <QStandardPaths>
35 #include <QUrl>
36 
37 #include <KLocalizedString>
38 #include <KMessageBox>
39 
40 #include "Exceptions.h"
41 #include "Symbol.h"
42 #include "SymbolLibrary.h"
43 
44 
45 SymbolManager *SymbolManager::symbolManager = nullptr;
46 
47 
48 /**
49  * Accessor for the static object
50  */
self()51 SymbolManager &SymbolManager::self()
52 {
53     if (symbolManager == nullptr) {
54         symbolManager = new SymbolManager();
55     }
56 
57     return *symbolManager;
58 }
59 
60 
61 /**
62  * Constructor.
63  */
SymbolManager()64 SymbolManager::SymbolManager()
65 {
66     refresh();
67 }
68 
69 
70 /**
71  *Destructor. Delete all the symbol libraries.
72  */
~SymbolManager()73 SymbolManager::~SymbolManager()
74 {
75     qDeleteAll(m_symbolLibraries);
76 }
77 
78 
79 /**
80  * Get a list of the symbol libraries available.
81  *
82  * @return QStringList of symbol library names.
83  */
libraries()84 QStringList SymbolManager::libraries()
85 {
86     QStringList libraryNames;
87 
88     QListIterator<SymbolLibrary *> i(self().m_symbolLibraries);
89 
90     while (i.hasNext()) {
91         libraryNames.append(i.next()->name());
92     }
93 
94     return libraryNames;
95 }
96 
97 
98 /**
99  * Get a pointer to a symbol library by name
100  *
101  * @param name of the library required.
102  *
103  * @return pointer to the SymbolLibrary instance, returns null if no library found.
104  */
library(const QString & name)105 SymbolLibrary *SymbolManager::library(const QString &name)
106 {
107     for (int i = 0 ; i < self().m_symbolLibraries.count() ; ++i) {
108         SymbolLibrary *symbolLibrary = self().m_symbolLibraries.at(i);
109 
110         if (symbolLibrary->name() == name) {
111             return symbolLibrary;
112         }
113     }
114 
115     return nullptr;
116 }
117 
118 
119 /**
120  * Get a list of files stored in the symbols path, iterating each one to create a new SymbolLibrary instance
121  * and read the library.
122  * Assumes that local resources are given before global ones and should take priority.
123  */
refresh()124 void SymbolManager::refresh()
125 {
126     const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::DataLocation, QStringLiteral("symbols"), QStandardPaths::LocateDirectory);
127 
128     Q_FOREACH (const QString &dir, dirs) {
129         QDirIterator it(dir, QStringList() << QStringLiteral("*.sym"));
130 
131         while (it.hasNext()) {
132             SymbolLibrary *symbolLibrary = readLibrary(it.next());
133 
134             if (symbolLibrary) {
135                 m_symbolLibraries.append(symbolLibrary);
136             }
137         }
138     }
139 }
140 
141 
142 /**
143  * Read a symbol library.
144  *
145  * @param name path to the symbol file to be read.
146  *
147  * @return pointer to the SymbolLibrary instance created.
148  */
readLibrary(const QString & name)149 SymbolLibrary *SymbolManager::readLibrary(const QString &name)
150 {
151     SymbolLibrary *symbolLibrary = new SymbolLibrary;
152 
153     QFile file(name);
154 
155     if (file.open(QIODevice::ReadOnly)) {
156         QDataStream stream(&file);
157 
158         try {
159             stream >> *symbolLibrary;
160             symbolLibrary->setName(QFileInfo(name).baseName());
161         } catch (const InvalidFile &e) {
162             KMessageBox::sorry(nullptr, i18n("This does not appear to be a valid symbol file"));
163             delete symbolLibrary;
164             symbolLibrary = nullptr;
165         } catch (const InvalidFileVersion &e) {
166             KMessageBox::sorry(nullptr, e.version);
167             delete symbolLibrary;
168             symbolLibrary = nullptr;
169         } catch (const FailedReadFile &e) {
170             KMessageBox::sorry(nullptr, e.status);
171             delete symbolLibrary;
172             symbolLibrary = nullptr;
173         }
174 
175         file.close();
176     } else {
177         KMessageBox::sorry(nullptr, i18n("Failed to open the file %1", name));
178         delete symbolLibrary;
179         symbolLibrary = nullptr;
180     }
181 
182     return symbolLibrary;
183 }
184