File indexing completed on 2025-03-16 13:13:04
0001 /******************************************************************************** 0002 * Copyright (C) 2011-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 0012 /** 0013 * @file 0014 * Implement the SymbolLibrary class 0015 */ 0016 0017 0018 /** 0019 * @page symbol_library Symbol Library 0020 * The symbols created are stored in symbol files. A number of different files can be created containing symbols 0021 * of different themes. When files are opened the symbol library will be populated with the contents of the file. 0022 * 0023 * The symbol library can be associated with a LibraryListWidget which will provide an icon view of all the 0024 * existing symbols. Clicking on one of the icons will open this symbol in the editor and allow it to be modified. 0025 * New symbols created can be saved to the library. 0026 * 0027 * Note that symbols saved to the library are not immediately saved to disk. The user is required to save the 0028 * library with the File->Save command. The user may also use the File->Save As command to save the library to a 0029 * different file. 0030 * 0031 * @image html ui-main-library.png "The user interface showing the library tab" 0032 * 0033 * All changes to the library through adding new symbols, editing a symbol or importing a library can be undone 0034 * or redone with the undo and redo commands. A separate undo stack is managed for the library and for the editor 0035 * and the active one is dependent on which of the tabs is selected. Undoing commands in the editor tab does not 0036 * affect the library, similarly undoing commands in the library does not affect the editor. 0037 * 0038 * A context menu is available for the symbols in the list allowing individual symbols to be deleted. This can be 0039 * undone if required. 0040 * 0041 * Using the File->Import Library it is also possible to import symbols from another symbol file into the current 0042 * symbol library. These will then be appended to the current set of symbols. 0043 * 0044 * The symbol library view also allows drag and drop of symbols between instances of the SymbolEditor, so if you 0045 * want to copy a number of symbols without importing them all, this can be done on an individual basis. 0046 * 0047 * File->Close will close the current library leaving a new empty library that new symbols can be added to. 0048 */ 0049 0050 0051 #include "SymbolLibrary.h" 0052 0053 #include <QDataStream> 0054 #include <QListWidgetItem> 0055 #include <QPainter> 0056 #include <QtAlgorithms> 0057 0058 #include <KLocalizedString> 0059 0060 #include "Exceptions.h" 0061 #include "SymbolListWidget.h" 0062 0063 0064 /** 0065 * Construct a SymbolLibrary. 0066 * Set the index to 1. 0067 * The index will be set when a file is loaded and will be incremented when new symbols 0068 * have been added. It will be saved with the file for the next time it is loaded. 0069 */ 0070 SymbolLibrary::SymbolLibrary(SymbolListWidget *listWidget) 0071 : m_listWidget(listWidget) 0072 { 0073 clear(); 0074 } 0075 0076 0077 /** 0078 * Destructor 0079 */ 0080 SymbolLibrary::~SymbolLibrary() 0081 { 0082 clear(); 0083 } 0084 0085 0086 /** 0087 * Clear the file of symbols. 0088 * Clears the undo stack, deletes all the QListWidgetItems and clears the symbol map. 0089 * The index is reset to 1. 0090 */ 0091 void SymbolLibrary::clear() 0092 { 0093 m_undoStack.clear(); 0094 0095 if (m_listWidget) { 0096 foreach (qint16 index, indexes()) { 0097 m_listWidget->removeSymbol(index); 0098 } 0099 } 0100 0101 m_symbols.clear(); 0102 m_nextIndex = 1; 0103 } 0104 0105 0106 /** 0107 * Get the path associated with an index. 0108 * If the index is not in the library it returns a default constructed Symbol. 0109 * 0110 * @param index a qint16 representing the index to find 0111 * 0112 * @return a Symbol 0113 */ 0114 Symbol SymbolLibrary::symbol(qint16 index) 0115 { 0116 return (m_symbols.contains(index) ? m_symbols[index] : Symbol()); 0117 } 0118 0119 0120 /** 0121 * Take a symbol from the library. 0122 * Remove a symbol identified by it's index and return it. 0123 * The QListWidgetItem associated with the symbol is also removed and deleted. 0124 * If the index is not in the library it returns a default constructed symbol. 0125 * 0126 * @param index the index of the Symbol to be removed 0127 * 0128 * @return a Symbol 0129 */ 0130 Symbol SymbolLibrary::takeSymbol(qint16 index) 0131 { 0132 Symbol symbol; 0133 0134 if (m_symbols.contains(index)) { 0135 symbol = m_symbols.take(index); 0136 0137 if (m_listWidget) { 0138 m_listWidget->removeSymbol(index); 0139 } 0140 } 0141 0142 return symbol; 0143 } 0144 0145 0146 /** 0147 * Update the Symbol for an index in the library. 0148 * If the index supplied is 0, a new index will be taken from the m_nextIndex value which is 0149 * then incremented. This value will be returned. 0150 * When a LibraryListWidget has been linked to the SymbolLibrary the symbol is added to the 0151 * LibraryListWidget. 0152 * 0153 * @param index a qint16 representing the index 0154 * @param symbol a const reference to a Symbol 0155 * 0156 * @return a qint16 representing the index, this is useful when the original index was 0 0157 */ 0158 qint16 SymbolLibrary::setSymbol(qint16 index, const Symbol &symbol) 0159 { 0160 if (!index) { 0161 index = m_nextIndex++; 0162 } 0163 0164 m_symbols.insert(index, symbol); 0165 0166 if (m_listWidget) { 0167 m_listWidget->addSymbol(index, symbol); 0168 } 0169 0170 return index; 0171 } 0172 0173 0174 /** 0175 * Get the name of the symbol library. 0176 */ 0177 QString SymbolLibrary::name() const 0178 { 0179 return m_name; 0180 } 0181 0182 0183 /** 0184 * Set the name of the symbol library. 0185 * 0186 * @param name the name to be set 0187 */ 0188 void SymbolLibrary::setName(const QString &name) 0189 { 0190 m_name = name; 0191 } 0192 0193 0194 /** 0195 * Get a sorted list of symbol indexes 0196 * 0197 * @return a QList<qint16> of sorted indexes 0198 */ 0199 QList<qint16> SymbolLibrary::indexes() const 0200 { 0201 QList<qint16> keys = m_symbols.keys(); 0202 qSort(keys.begin(), keys.end()); 0203 return keys; 0204 } 0205 0206 0207 /** 0208 * Get a pointer to the symbol library undo stack. 0209 * 0210 * @return a pointer to a QUndoStack 0211 */ 0212 QUndoStack *SymbolLibrary::undoStack() 0213 { 0214 return &m_undoStack; 0215 } 0216 0217 0218 /** 0219 * Generate all the items in the library. 0220 * This will be called when a library file is loaded to generate all the new 0221 * QListWidgetItems for the symbols in the library and generate an icon for it. 0222 * A list of sorted QMap keys is retrieved to allow adding items in the correct 0223 * order. 0224 */ 0225 void SymbolLibrary::generateItems() 0226 { 0227 if (!m_listWidget) { 0228 return; 0229 } 0230 0231 m_listWidget->loadFromLibrary(this); 0232 } 0233 0234 0235 /** 0236 * Stream out the file. 0237 * Symbol files are indicated with a magic string of KXStitchSymbols. The stream version is set 0238 * to maintain consistency with the streamed objects. 0239 * Write the version, current index and the map of symbols. 0240 * 0241 * @param stream a reference to a QDataStream 0242 * @param library a const reference to a SymbolLibrary 0243 * 0244 * @return a reference to a QDataStream 0245 */ 0246 QDataStream &operator<<(QDataStream &stream, const SymbolLibrary &library) 0247 { 0248 qint16 lastIndex = 0; 0249 0250 QMapIterator<qint16, Symbol> i(library.m_symbols); 0251 0252 while (i.hasNext()) { 0253 i.next(); 0254 if (i.key() > lastIndex) { 0255 lastIndex = i.key(); 0256 } 0257 } 0258 0259 lastIndex++; 0260 0261 stream.writeRawData("KXStitchSymbols", 15); 0262 stream.setVersion(QDataStream::Qt_4_0); 0263 stream << library.version; 0264 stream << lastIndex; 0265 0266 if (stream.status() != QDataStream::Ok) { 0267 throw FailedWriteLibrary(stream.status()); 0268 } 0269 0270 stream << library.m_symbols; 0271 return stream; 0272 } 0273 0274 0275 /** 0276 * Stream in the file. 0277 * Initially clear the current contents. 0278 * Symbol files are indicated with a magic string of KXStitchSymbols. The stream version is set 0279 * to maintain consistency with the streamed objects. Read and check the magic string. If this 0280 * is not a symbol file throw an exception. 0281 * Read in the version. 0282 * Read the data for the specified version. The stream is checked for errors and an exception is 0283 * thrown if there was an error. 0284 * 0285 * @param stream a reference to a QDataStream 0286 * @param library a reference to a SymbolLibrary 0287 * 0288 * @return a reference to a QDataStream 0289 */ 0290 QDataStream &operator>>(QDataStream &stream, SymbolLibrary &library) 0291 { 0292 library.clear(); 0293 0294 char magic[15]; 0295 stream.readRawData(magic, 15); 0296 0297 if (strncmp(magic, "KXStitchSymbols", 15) == 0) { 0298 stream.setVersion(QDataStream::Qt_4_0); 0299 qint32 version; 0300 QMap<qint16, QPainterPath> paths_v100; 0301 QList<qint16> paths_v100_keys; 0302 stream >> version; 0303 0304 switch (version) { 0305 case 101: 0306 stream >> library.m_nextIndex; 0307 0308 if (stream.status() != QDataStream::Ok) { 0309 throw FailedReadLibrary(stream.status()); 0310 } 0311 0312 stream >> library.m_symbols; 0313 library.generateItems(); 0314 break; 0315 0316 case 100: 0317 stream >> library.m_nextIndex; 0318 library.m_nextIndex++; 0319 stream >> paths_v100; 0320 paths_v100_keys = paths_v100.keys(); 0321 0322 foreach (qint16 index, paths_v100_keys) { 0323 Symbol symbol; 0324 symbol.setPath(paths_v100[index]); 0325 library.m_symbols.insert(index, symbol); 0326 } 0327 0328 library.generateItems(); 0329 break; 0330 0331 default: 0332 throw InvalidFileVersion(version); 0333 break; 0334 } 0335 } else { 0336 throw InvalidFile(); 0337 } 0338 0339 return stream; 0340 }