File indexing completed on 2024-05-05 08:41:23

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