File indexing completed on 2024-05-12 15:26:39
0001 /*************************************************************************** 0002 File : Folder.cpp 0003 Project : LabPlot 0004 Description : Folder in a project 0005 -------------------------------------------------------------------- 0006 Copyright : (C) 2009-2015 Alexander Semke (alexander.semke@web.de) 0007 Copyright : (C) 2007 Tilman Benkert (thzs@gmx.net) 0008 Copyright : (C) 2007 Knut Franke (knut.franke@gmx.de) 0009 0010 ***************************************************************************/ 0011 0012 /*************************************************************************** 0013 * * 0014 * This program is free software; you can redistribute it and/or modify * 0015 * it under the terms of the GNU General Public License as published by * 0016 * the Free Software Foundation; either version 2 of the License, or * 0017 * (at your option) any later version. * 0018 * * 0019 * This program is distributed in the hope that it will be useful, * 0020 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0021 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0022 * GNU General Public License for more details. * 0023 * * 0024 * You should have received a copy of the GNU General Public License * 0025 * along with this program; if not, write to the Free Software * 0026 * Foundation, Inc., 51 Franklin Street, Fifth Floor, * 0027 * Boston, MA 02110-1301 USA * 0028 * * 0029 ***************************************************************************/ 0030 0031 #include "backend/core/Folder.h" 0032 #include "backend/datapicker/Datapicker.h" 0033 #include "backend/core/Project.h" 0034 #include "backend/core/Workbook.h" 0035 #include "backend/core/column/Column.h" 0036 #include "backend/datasources/LiveDataSource.h" 0037 #include "backend/matrix/Matrix.h" 0038 #include "backend/note/Note.h" 0039 #include "backend/spreadsheet/Spreadsheet.h" 0040 #ifdef HAVE_CANTOR_LIBS 0041 #include "backend/cantorWorksheet/CantorWorksheet.h" 0042 #endif 0043 #ifdef HAVE_MQTT 0044 #include "backend/datasources/MQTTClient.h" 0045 #endif 0046 #include "backend/worksheet/Worksheet.h" 0047 0048 #include <QDropEvent> 0049 #include <QIcon> 0050 #include <QMimeData> 0051 #include <KLocalizedString> 0052 0053 /** 0054 * \class Folder 0055 * \brief Folder in a project 0056 */ 0057 0058 Folder::Folder(const QString &name, AspectType type) : AbstractAspect(name, type) { 0059 } 0060 0061 QIcon Folder::icon() const { 0062 return QIcon::fromTheme("folder"); 0063 } 0064 0065 /** 0066 * \brief Return a new context menu. 0067 * 0068 * The caller takes ownership of the menu. 0069 */ 0070 QMenu* Folder::createContextMenu() { 0071 if (project() 0072 #ifdef HAVE_MQTT 0073 && type() != AspectType::MQTTSubscription 0074 #endif 0075 ) 0076 return project()->createFolderContextMenu(this); 0077 return nullptr; 0078 } 0079 0080 bool Folder::isDraggable() const { 0081 if (dynamic_cast<const Project*>(this)) 0082 return false; 0083 else 0084 return true; 0085 } 0086 0087 QVector<AspectType> Folder::dropableOn() const { 0088 return QVector<AspectType>{AspectType::Folder, AspectType::Project}; 0089 } 0090 0091 void Folder::processDropEvent(const QVector<quintptr>& vec) { 0092 //reparent AbstractPart and Folder objects only 0093 AbstractAspect* lastMovedAspect{nullptr}; 0094 for (auto a : vec) { 0095 auto* aspect = reinterpret_cast<AbstractAspect*>(a); 0096 auto* part = dynamic_cast<AbstractPart*>(aspect); 0097 if (part) { 0098 part->reparent(this); 0099 lastMovedAspect = part; 0100 } else { 0101 auto* folder = dynamic_cast<Folder*>(aspect); 0102 if (folder && folder != this) { 0103 folder->reparent(this); 0104 lastMovedAspect = folder; 0105 } 0106 } 0107 } 0108 0109 //select the last moved aspect in the project explorer 0110 if (lastMovedAspect) 0111 lastMovedAspect->setSelected(true); 0112 } 0113 0114 /** 0115 * \brief Save as XML 0116 */ 0117 void Folder::save(QXmlStreamWriter* writer) const { 0118 writer->writeStartElement(QLatin1String("folder")); 0119 writeBasicAttributes(writer); 0120 writeCommentElement(writer); 0121 0122 for (auto* child : children<AbstractAspect>(ChildIndexFlag::IncludeHidden)) { 0123 writer->writeStartElement(QLatin1String("child_aspect")); 0124 child->save(writer); 0125 writer->writeEndElement(); // "child_aspect" 0126 } 0127 writer->writeEndElement(); // "folder" 0128 } 0129 0130 /** 0131 * \brief Load from XML 0132 */ 0133 bool Folder::load(XmlStreamReader* reader, bool preview) { 0134 if (!readBasicAttributes(reader)) 0135 return false; 0136 0137 // read child elements 0138 while (!reader->atEnd()) { 0139 reader->readNext(); 0140 0141 if (reader->isEndElement()) break; 0142 0143 if (reader->isStartElement()) { 0144 if (reader->name() == QLatin1String("comment")) { 0145 if (!readCommentElement(reader)) 0146 return false; 0147 } else if (reader->name() == QLatin1String("child_aspect")) { 0148 if (!readChildAspectElement(reader, preview)) 0149 return false; 0150 } else {// unknown element 0151 reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); 0152 if (!reader->skipToEndElement()) return false; 0153 } 0154 } 0155 } 0156 0157 return !reader->hasError(); 0158 } 0159 0160 void Folder::setPathesToLoad(const QStringList& pathes) { 0161 m_pathesToLoad = pathes; 0162 } 0163 0164 const QStringList& Folder::pathesToLoad() const { 0165 return m_pathesToLoad; 0166 } 0167 0168 /** 0169 * \brief Read child aspect from XML 0170 */ 0171 bool Folder::readChildAspectElement(XmlStreamReader* reader, bool preview) { 0172 if (!reader->skipToNextTag()) 0173 return false; 0174 0175 if (reader->isEndElement() && reader->name() == QLatin1String("child_aspect")) 0176 return true; // empty element tag 0177 0178 //check whether we need to skip the loading of the current child aspect 0179 if (!m_pathesToLoad.isEmpty()) { 0180 const QString& name = reader->attributes().value("name").toString(); //name of the current child aspect 0181 const QString childPath = path() + '/' + name; //child's path is not available yet (child not added yet) -> construct it manually 0182 0183 //skip the current child aspect it is not in the list of aspects to be loaded 0184 if (m_pathesToLoad.indexOf(childPath) == -1) { 0185 //skip to the end of the current element 0186 if (!reader->skipToEndElement()) 0187 return false; 0188 0189 //skip to the end of the "child_asspect" element 0190 if (!reader->skipToEndElement()) 0191 return false; 0192 return true; 0193 } 0194 } 0195 0196 QString element_name = reader->name().toString(); 0197 if (element_name == QLatin1String("folder")) { 0198 Folder* folder = new Folder(QString()); 0199 0200 if (!m_pathesToLoad.isEmpty()) { 0201 //a child folder to be read -> provide the list of aspects to be loaded to the child folder, too. 0202 //since the child folder and all its children are not added yet (path() returns empty string), 0203 //we need to remove the path of the current child folder from the full pathes provided in m_pathesToLoad. 0204 //E.g. we want to import the path "Project/Folder/Spreadsheet" in the following project 0205 // Project 0206 // \Spreadsheet 0207 // \Folder 0208 // \Spreadsheet 0209 // 0210 //Here, we remove the part "Project/Folder/" and proceed for this child folder with "Spreadsheet" only. 0211 //With this the logic above where it is determined whether to import the child aspect or not works out. 0212 0213 //manually construct the path of the child folder to be read 0214 const QString& curFolderPath = path() + '/' + reader->attributes().value("name").toString(); 0215 0216 //remove the path of the current child folder 0217 QStringList pathesToLoadNew; 0218 for (auto path : m_pathesToLoad) { 0219 if (path.startsWith(curFolderPath)) 0220 pathesToLoadNew << path.right(path.length() - curFolderPath.length()); 0221 } 0222 0223 folder->setPathesToLoad(pathesToLoadNew); 0224 } 0225 0226 if (!folder->load(reader, preview)) { 0227 delete folder; 0228 return false; 0229 } 0230 addChildFast(folder); 0231 } else if (element_name == QLatin1String("workbook")) { 0232 Workbook* workbook = new Workbook(QString()); 0233 if (!workbook->load(reader, preview)) { 0234 delete workbook; 0235 return false; 0236 } 0237 addChildFast(workbook); 0238 } else if (element_name == QLatin1String("spreadsheet")) { 0239 Spreadsheet* spreadsheet = new Spreadsheet(QString(), true); 0240 if (!spreadsheet->load(reader, preview)) { 0241 delete spreadsheet; 0242 return false; 0243 } 0244 addChildFast(spreadsheet); 0245 } else if (element_name == QLatin1String("matrix")) { 0246 Matrix* matrix = new Matrix(QString(), true); 0247 if (!matrix->load(reader, preview)) { 0248 delete matrix; 0249 return false; 0250 } 0251 addChildFast(matrix); 0252 } else if (element_name == QLatin1String("worksheet")) { 0253 Worksheet* worksheet = new Worksheet(QString()); 0254 worksheet->setIsLoading(true); 0255 if (!worksheet->load(reader, preview)) { 0256 delete worksheet; 0257 return false; 0258 } 0259 addChildFast(worksheet); 0260 worksheet->setIsLoading(false); 0261 } else if (element_name == QLatin1String("cantorWorksheet")) { 0262 #ifdef HAVE_CANTOR_LIBS 0263 CantorWorksheet* cantorWorksheet = new CantorWorksheet(QLatin1String("null"), true); 0264 if (!cantorWorksheet->load(reader, preview)) { 0265 delete cantorWorksheet; 0266 0267 //if we only failed to load because of the missing CAS, don't return with false here. 0268 //in this case we continue loading the project and show a warning about missing CAS at the end. 0269 if (!reader->failedCASMissing()) 0270 return false; 0271 else { 0272 //failed because of the missing CAS. Read until the end of the current 0273 //element in XML and continue loading the project. 0274 while (!reader->atEnd()) { 0275 reader->readNext(); 0276 if (reader->isEndElement() && reader->name() == QLatin1String("cantorWorksheet")) 0277 break; 0278 } 0279 } 0280 } else 0281 addChildFast(cantorWorksheet); 0282 #else 0283 if (!preview) { 0284 while (!reader->atEnd()) { 0285 reader->readNext(); 0286 if (reader->isEndElement() && reader->name() == QLatin1String("cantorWorksheet")) 0287 break; 0288 0289 if (!reader->isStartElement()) 0290 continue; 0291 0292 if (reader->name() == QLatin1String("general")) { 0293 const QString& backendName = reader->attributes().value("backend_name").toString().trimmed(); 0294 if (!backendName.isEmpty()) 0295 reader->raiseMissingCASWarning(backendName); 0296 } else 0297 reader->skipToEndElement(); 0298 } 0299 } 0300 #endif 0301 #ifdef HAVE_MQTT 0302 } else if (element_name == QLatin1String("MQTTClient")) { 0303 MQTTClient* client = new MQTTClient(QString()); 0304 if (!client->load(reader, preview)) { 0305 delete client; 0306 return false; 0307 } 0308 addChildFast(client); 0309 #endif 0310 } else if (element_name == QLatin1String("liveDataSource") 0311 || element_name == QLatin1String("LiveDataSource")) { //TODO: remove "LiveDataSources" in couple of releases 0312 auto* liveDataSource = new LiveDataSource(QString(), true); 0313 if (!liveDataSource->load(reader, preview)) { 0314 delete liveDataSource; 0315 return false; 0316 } 0317 addChildFast(liveDataSource); 0318 } else if (element_name == QLatin1String("datapicker")) { 0319 Datapicker* datapicker = new Datapicker(QString(), true); 0320 if (!datapicker->load(reader, preview)) { 0321 delete datapicker; 0322 return false; 0323 } 0324 addChildFast(datapicker); 0325 } else if (element_name == QLatin1String("note")) { 0326 Note* note = new Note(QString()); 0327 if (!note->load(reader, preview)) { 0328 delete note; 0329 return false; 0330 } 0331 addChildFast(note); 0332 } else { 0333 reader->raiseWarning(i18n("unknown element '%1' found", element_name)); 0334 if (!reader->skipToEndElement()) 0335 return false; 0336 } 0337 0338 if (!reader->skipToNextTag()) return false; 0339 return !reader->hasError(); 0340 }