Warning, file /office/calligra/libs/flake/KoShapeRegistry.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* This file is part of the KDE project 0002 * Copyright (c) 2006 Boudewijn Rempt (boud@valdyas.org) 0003 * Copyright (C) 2006-2007, 2010 Thomas Zander <zander@kde.org> 0004 * Copyright (C) 2006,2008-2010 Thorsten Zachmann <zachmann@kde.org> 0005 * Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net> 0006 * Copyright (C) 2010 Inge Wallin <inge@lysator.liu.se> 0007 * 0008 * This library is free software; you can redistribute it and/or 0009 * modify it under the terms of the GNU Library General Public 0010 * License as published by the Free Software Foundation; either 0011 * version 2 of the License, or (at your option) any later version. 0012 * 0013 * This library is distributed in the hope that it will be useful, 0014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0016 * Library General Public License for more details. 0017 * 0018 * You should have received a copy of the GNU Library General Public License 0019 * along with this library; see the file COPYING.LIB. If not, write to 0020 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0021 * Boston, MA 02110-1301, USA. 0022 */ 0023 0024 // Own 0025 #include "KoShapeRegistry.h" 0026 0027 #include "KoPathShapeFactory.h" 0028 #include "KoConnectionShapeFactory.h" 0029 #include "KoShapeLoadingContext.h" 0030 #include "KoShapeSavingContext.h" 0031 #include "KoShapeGroup.h" 0032 #include "KoShapeLayer.h" 0033 #include "KoUnavailShape.h" 0034 #include "SvgShapeFactory.h" 0035 0036 #include <KoPluginLoader.h> 0037 #include <KoXmlReader.h> 0038 #include <KoXmlNS.h> 0039 #include <KoOdfLoadingContext.h> 0040 #include <KoStyleStack.h> 0041 0042 #include <QString> 0043 #include <QHash> 0044 #include <QMultiMap> 0045 #include <QPainter> 0046 #include <QGlobalStatic> 0047 0048 #include <FlakeDebug.h> 0049 0050 Q_GLOBAL_STATIC(KoShapeRegistry, s_instance) 0051 0052 class Q_DECL_HIDDEN KoShapeRegistry::Private 0053 { 0054 public: 0055 void insertFactory(KoShapeFactoryBase *factory); 0056 void init(KoShapeRegistry *q); 0057 0058 KoShape *createShapeInternal(const KoXmlElement &fullElement, KoShapeLoadingContext &context, const KoXmlElement &element) const; 0059 0060 // Map namespace,tagname to priority:factory 0061 QHash<QPair<QString, QString>, QMultiMap<int, KoShapeFactoryBase*> > factoryMap; 0062 }; 0063 0064 KoShapeRegistry::KoShapeRegistry() 0065 : d(new Private()) 0066 { 0067 } 0068 0069 KoShapeRegistry::~KoShapeRegistry() 0070 { 0071 qDeleteAll(doubleEntries()); 0072 qDeleteAll(values()); 0073 delete d; 0074 } 0075 0076 void KoShapeRegistry::Private::init(KoShapeRegistry *q) 0077 { 0078 KoPluginLoader::PluginsConfig config; 0079 config.whiteList = "FlakePlugins"; 0080 config.blacklist = "FlakePluginsDisabled"; 0081 config.group = "calligra"; 0082 KoPluginLoader::load(QStringLiteral("calligra/flakes"), config); 0083 config.whiteList = "ShapePlugins"; 0084 config.blacklist = "ShapePluginsDisabled"; 0085 KoPluginLoader::load(QStringLiteral("calligra/shapes"), config); 0086 0087 // Also add our hard-coded basic shapes 0088 q->add(new KoPathShapeFactory(QStringList())); 0089 q->add(new KoConnectionShapeFactory()); 0090 // As long as there is no shape dealing with embedded svg images 0091 // we add the svg shape factory here by default 0092 q->add(new SvgShapeFactory); 0093 0094 // Now all shape factories are registered with us, determine their 0095 // associated odf tagname & priority and prepare ourselves for 0096 // loading ODF. 0097 0098 QList<KoShapeFactoryBase*> factories = q->values(); 0099 for (int i = 0; i < factories.size(); ++i) { 0100 insertFactory(factories[i]); 0101 } 0102 } 0103 0104 KoShapeRegistry* KoShapeRegistry::instance() 0105 { 0106 if (!s_instance.exists()) { 0107 s_instance->d->init(s_instance); 0108 } 0109 return s_instance; 0110 } 0111 0112 void KoShapeRegistry::addFactory(KoShapeFactoryBase * factory) 0113 { 0114 add(factory); 0115 d->insertFactory(factory); 0116 } 0117 0118 void KoShapeRegistry::Private::insertFactory(KoShapeFactoryBase *factory) 0119 { 0120 const QList<QPair<QString, QStringList> > odfElements(factory->odfElements()); 0121 0122 if (odfElements.isEmpty()) { 0123 debugFlake << "Shape factory" << factory->id() << " does not have OdfNamespace defined, ignoring"; 0124 } 0125 else { 0126 int priority = factory->loadingPriority(); 0127 for (QList<QPair<QString, QStringList> >::const_iterator it(odfElements.begin()); it != odfElements.end(); ++it) { 0128 foreach (const QString &elementName, (*it).second) { 0129 QPair<QString, QString> p((*it).first, elementName); 0130 0131 QMultiMap<int, KoShapeFactoryBase*> & priorityMap = factoryMap[p]; 0132 0133 priorityMap.insert(priority, factory); 0134 0135 debugFlake << "Inserting factory" << factory->id() << " for" 0136 << p << " with priority " 0137 << priority << " into factoryMap making " 0138 << priorityMap.size() << " entries. "; 0139 } 0140 } 0141 } 0142 } 0143 0144 KoShape * KoShapeRegistry::createShapeFromOdf(const KoXmlElement & e, KoShapeLoadingContext & context) const 0145 { 0146 debugFlake << "Going to check for" << e.namespaceURI() << ":" << e.tagName(); 0147 0148 KoShape * shape = 0; 0149 0150 // Handle the case where the element is a draw:frame differently from other cases. 0151 if (e.tagName() == "frame" && e.namespaceURI() == KoXmlNS::draw) { 0152 // If the element is in a frame, the frame is already added by the 0153 // application and we only want to create a shape from the 0154 // embedded element. The very first shape we create is accepted. 0155 // 0156 // FIXME: we might want to have some code to determine which is 0157 // the "best" of the creatable shapes. 0158 0159 // The logic is thus: 0160 // First attempt to check whether we can in fact load the first child, 0161 // and only use a Shape if the first child is accepted. If this is not the case, then 0162 // use the UnavailShape which ensures data integrity and that the fallback views are not 0163 // edited and subsequently saved back (as they would then no longer be a true 0164 // representation of the data they are supposed to be views of). 0165 // The reason is that all subsequent children will be fallbacks, in order of preference. 0166 0167 if (e.hasChildNodes()) { 0168 // if we don't ignore white spaces it can be that the first child is not a element so look for the first element 0169 KoXmlNode node = e.firstChild(); 0170 KoXmlElement element; 0171 while (!node.isNull() && element.isNull()) { 0172 element = node.toElement(); 0173 node = node.nextSibling(); 0174 } 0175 0176 if (!element.isNull()) { 0177 // Check for draw:object 0178 if (element.tagName() == "object" && element.namespaceURI() == KoXmlNS::draw && element.hasChildNodes()) { 0179 // Loop through the elements and find the first one 0180 // that is handled by any shape. 0181 KoXmlNode n = element.firstChild(); 0182 for (; !n.isNull(); n = n.nextSibling()) { 0183 if (n.isElement()) { 0184 debugFlake << "trying for element " << n.toElement().tagName(); 0185 shape = d->createShapeInternal(e, context, n.toElement()); 0186 if (shape) 0187 break; 0188 } 0189 } 0190 if (shape) 0191 debugFlake << "Found a shape for draw:object"; 0192 else 0193 debugFlake << "Found NO shape shape for draw:object"; 0194 } 0195 0196 if (!shape) { 0197 // If not draw:object, e.g draw:image or draw:plugin 0198 shape = d->createShapeInternal(e, context, element); 0199 } 0200 } 0201 0202 if (shape) { 0203 debugFlake << "A shape supporting the requested type was found."; 0204 } 0205 else { 0206 // If none of the registered shapes could handle the frame 0207 // contents, create an UnavailShape. This should never fail. 0208 debugFlake << "No shape found; Creating an unavail shape"; 0209 0210 KoUnavailShape *uShape = new KoUnavailShape(); 0211 uShape->setShapeId(KoUnavailShape_SHAPEID); 0212 //FIXME: Add creating/setting the collection here(?) 0213 uShape->loadOdf(e, context); 0214 0215 // Check whether we can load a shape to fit the current object. 0216 KoXmlElement child; 0217 KoShape *childShape = 0; 0218 bool first = true; 0219 forEachElement(child, e) { 0220 // no need to try to load the first element again as it was already tried before and we could not load it 0221 if (first) { 0222 first = false; 0223 continue; 0224 } 0225 debugFlake << "--------------------------------------------------------"; 0226 debugFlake << "Attempting to check if we can fall back ability to the item" 0227 << child.nodeName(); 0228 childShape = d->createShapeInternal(e, context, child); 0229 if (childShape) { 0230 debugFlake << "Shape was found! Adding as child of unavail shape and stopping search"; 0231 uShape->addShape(childShape); 0232 childShape->setPosition(QPointF(qreal(0.0), qreal(0.0))); 0233 0234 // The embedded shape is just there to show the preview image. 0235 // We don't want the user to be able to manipulate the picture 0236 // in any way, so we disable the tools of the shape. This can 0237 // be done in a hacky way (courtesy of Jaham) by setting its 0238 // shapeID to "". 0239 childShape->setShapeId(""); 0240 break; 0241 } 0242 } 0243 if (!childShape) 0244 debugFlake << "Failed to find fallback for the unavail shape named " 0245 << e.tagName(); 0246 shape = uShape; 0247 } 0248 } 0249 } 0250 0251 // Hardwire the group shape into the loading as it should not appear 0252 // in the shape selector 0253 else if (e.localName() == "g" && e.namespaceURI() == KoXmlNS::draw) { 0254 KoShapeGroup * group = new KoShapeGroup(); 0255 0256 context.odfLoadingContext().styleStack().save(); 0257 bool loaded = group->loadOdf(e, context); 0258 context.odfLoadingContext().styleStack().restore(); 0259 0260 if (loaded) { 0261 shape = group; 0262 } 0263 else { 0264 delete group; 0265 } 0266 } else { 0267 shape = d->createShapeInternal(e, context, e); 0268 } 0269 0270 if (shape) { 0271 context.shapeLoaded(shape); 0272 } 0273 0274 return shape; 0275 } 0276 0277 KoShape *KoShapeRegistry::Private::createShapeInternal(const KoXmlElement &fullElement, 0278 KoShapeLoadingContext &context, 0279 const KoXmlElement &element) const 0280 { 0281 // Pair of namespace, tagname 0282 QPair<QString, QString> p = QPair<QString, QString>(element.namespaceURI(), element.tagName()); 0283 0284 // Remove duplicate lookup. 0285 if (!factoryMap.contains(p)) 0286 return 0; 0287 0288 QMultiMap<int, KoShapeFactoryBase*> priorityMap = factoryMap.value(p); 0289 QList<KoShapeFactoryBase*> factories = priorityMap.values(); 0290 0291 #ifndef NDEBUG 0292 debugFlake << "Supported factories for=" << p; 0293 foreach (KoShapeFactoryBase *f, factories) 0294 debugFlake << f->id() << f->name(); 0295 #endif 0296 0297 // Loop through all shape factories. If any of them supports this 0298 // element, then we let the factory create a shape from it. This 0299 // may fail because the element itself is too generic to draw any 0300 // real conclusions from it - we actually have to try to load it. 0301 // An example of this is the draw:image element which have 0302 // potentially hundreds of different image formats to support, 0303 // including vector formats. 0304 // 0305 // If it succeeds, then we use this shape, if it fails, then just 0306 // try the next. 0307 // 0308 // Higher numbers are more specific, map is sorted by keys. 0309 for (int i = factories.size() - 1; i >= 0; --i) { 0310 KoShapeFactoryBase * factory = factories[i]; 0311 if (factory->supports(element, context)) { 0312 KoShape *shape = factory->createShapeFromOdf(fullElement, context); 0313 if (shape) { 0314 debugFlake << "Shape found for factory " << factory->id() << factory->name(); 0315 // we return the top-level most shape as thats the one that we'll have to 0316 // add to the KoShapeManager for painting later (and also to avoid memory leaks) 0317 // but don't go past a KoShapeLayer as KoShape adds those from the context 0318 // during loading and those are already added. 0319 while (shape->parent() && dynamic_cast<KoShapeLayer*>(shape->parent()) == 0) 0320 shape = shape->parent(); 0321 0322 return shape; 0323 } 0324 // Maybe a shape with a lower priority can load our 0325 // element, but this attempt has failed. 0326 } 0327 else { 0328 debugFlake << "No support for" << p << "by" << factory->id(); 0329 } 0330 } 0331 0332 return 0; 0333 } 0334 0335 QList<KoShapeFactoryBase*> KoShapeRegistry::factoriesForElement(const QString &nameSpace, const QString &elementName) 0336 { 0337 // Pair of namespace, tagname 0338 QPair<QString, QString> p = QPair<QString, QString>(nameSpace, elementName); 0339 0340 QMultiMap<int, KoShapeFactoryBase*> priorityMap = d->factoryMap.value(p); 0341 QList<KoShapeFactoryBase*> shapeFactories; 0342 // sort list by priority 0343 foreach(KoShapeFactoryBase *f, priorityMap) { 0344 shapeFactories.prepend(f); 0345 } 0346 0347 return shapeFactories; 0348 }