File indexing completed on 2024-04-14 14:47:09

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 }