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