File indexing completed on 2024-05-12 16:34:02

0001 /* This file is part of the KDE project
0002    Copyright (C) 2006 Martin Pfeiffer <hubipete@gmx.net>
0003                  2009 Jeremias Epperlein <jeeree@web.de>
0004 
0005    This library is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License as published by the Free Software Foundation; either
0008    version 2 of the License, or (at your option) any later version.
0009 
0010    This library is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this library; see the file COPYING.LIB.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018    Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "KoFormulaShape.h"
0022 
0023 #include <QUrl>
0024 
0025 #include <kmessagebox.h>
0026 #include <kguiitem.h>
0027 
0028 #include <KoStore.h>
0029 #include <KoShapeSavingContext.h>
0030 #include <KoShapeLoadingContext.h>
0031 #include <KoOdfLoadingContext.h>
0032 #include <KoXmlWriter.h>
0033 #include <KoXmlReader.h>
0034 #include <KoXmlNS.h>
0035 #include <KoDocumentResourceManager.h>
0036 
0037 #include "FormulaDebug.h"
0038 #include "FormulaDocument.h"
0039 #include "FormulaData.h"
0040 #include "FormulaElement.h"
0041 #include "FormulaRenderer.h"
0042 
0043 
0044 KoFormulaShape::KoFormulaShape(KoDocumentResourceManager *documentResourceManager)
0045   : KoFrameShape( KoXmlNS::draw, "object" )
0046 {
0047     FormulaElement* element= new FormulaElement();
0048     m_formulaData = new FormulaData(element);
0049     m_formulaRenderer = new FormulaRenderer();
0050     m_isInline = false;
0051 
0052     m_document = new FormulaDocument( this );
0053     m_resourceManager = documentResourceManager;
0054 }
0055 
0056 KoFormulaShape::~KoFormulaShape()
0057 {
0058     delete m_formulaData;
0059     delete m_formulaRenderer;
0060 }
0061 
0062 void KoFormulaShape::paint( QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &)
0063 {
0064     painter.save();
0065     applyConversion( painter, converter );   // apply zooming and coordinate translation
0066     m_formulaRenderer->layoutElement(  m_formulaData->formulaElement() );
0067     m_formulaRenderer->paintElement( painter,  m_formulaData->formulaElement() );  // paint the formula
0068     painter.restore();
0069 }
0070 
0071 void KoFormulaShape::updateLayout() {
0072     m_formulaRenderer->layoutElement( m_formulaData->formulaElement() );
0073 
0074      KoShape::setSize(m_formulaData->formulaElement()->boundingRect().size());
0075 }
0076 
0077 
0078 BasicElement* KoFormulaShape::elementAt( const QPointF& p )
0079 {
0080     return m_formulaData->formulaElement()->childElementAt( p );
0081 }
0082 
0083 void KoFormulaShape::resize( const QSizeF& )
0084 { /* do nothing as FormulaShape is fixed size */ }
0085 
0086 FormulaData* KoFormulaShape::formulaData() const
0087 {
0088     return  m_formulaData;
0089 }
0090 
0091 FormulaRenderer* KoFormulaShape::formulaRenderer() const
0092 {
0093     return m_formulaRenderer;
0094 }
0095 
0096 bool KoFormulaShape::loadOdf( const KoXmlElement& element, KoShapeLoadingContext &context )
0097 {
0098     debugFormula <<"Loading ODF in Formula";
0099     loadOdfAttributes(element, context, OdfAllAttributes);
0100     return loadOdfFrame(element, context);
0101 }
0102 
0103 bool KoFormulaShape::loadOdfFrameElement(const KoXmlElement &element,
0104                                          KoShapeLoadingContext &context)
0105 {
0106     // If this formula is embedded and not inline, then load the embedded document.
0107     if ( element.tagName() == "object" && element.hasAttributeNS( KoXmlNS::xlink, "href" )) {
0108         m_isInline = false;
0109 
0110         // This calls loadOdfEmbedded().
0111         return loadEmbeddedDocument( context.odfLoadingContext().store(),
0112                                      element,
0113                                      context.odfLoadingContext() );
0114     }
0115 
0116     // It's not a frame:object, so it must be inline.
0117     const KoXmlElement& topLevelElement = KoXml::namedItemNS(element, KoXmlNS::math, "math");
0118     if (topLevelElement.isNull()) {
0119         warnFormula << "no math element as first child";
0120         return false;
0121     }
0122 
0123     // Create a new root element, load the formula and replace the old one.
0124     FormulaElement* formulaElement = new FormulaElement();
0125     formulaElement->readMathML( topLevelElement );
0126     delete m_formulaData->formulaElement();
0127     m_formulaData->setFormulaElement(formulaElement);
0128     m_formulaData->notifyDataChange(0, false);
0129 
0130     m_isInline = true;
0131 
0132     return true;
0133 }
0134 
0135 bool KoFormulaShape::loadEmbeddedDocument( KoStore *store,
0136                                            const KoXmlElement &objectElement,
0137                                            const KoOdfLoadingContext &odfLoadingContext)
0138 {
0139     if ( !objectElement.hasAttributeNS( KoXmlNS::xlink, "href" ) ) {
0140         errorFormula << "Object element has no valid xlink:href attribute";
0141         return false;
0142     }
0143 
0144     QString url = objectElement.attributeNS( KoXmlNS::xlink, "href" );
0145 
0146     // It can happen that the url is empty e.g. when it is a
0147     // presentation:placeholder.
0148     if ( url.isEmpty() ) {
0149         return true;
0150     }
0151 
0152     QString tmpURL;
0153     if ( url[0] == '#' )
0154         url.remove( 0, 1 );
0155 
0156 #define INTERNAL_PROTOCOL "intern"
0157 #define STORE_PROTOCOL "tar"
0158 
0159     if (QUrl::fromUserInput(url).isRelative()) {
0160         if ( url.startsWith( "./" ) )
0161             tmpURL = QString( INTERNAL_PROTOCOL ) + ":/" + url.mid( 2 );
0162         else
0163             tmpURL = QString( INTERNAL_PROTOCOL ) + ":/" + url;
0164     }
0165     else
0166         tmpURL = url;
0167 
0168     QString path = tmpURL;
0169     if ( tmpURL.startsWith( INTERNAL_PROTOCOL ) ) {
0170         path = store->currentPath();
0171         if ( !path.isEmpty() && !path.endsWith( '/' ) )
0172             path += '/';
0173         QString relPath = QUrl::fromUserInput(tmpURL).path();
0174         path += relPath.mid( 1 ); // remove leading '/'
0175     }
0176     if ( !path.endsWith( '/' ) )
0177         path += '/';
0178 
0179     const QString mimeType = odfLoadingContext.mimeTypeForPath( path );
0180     //debugFormula << "path for manifest file=" << path << "mimeType=" << mimeType;
0181     if ( mimeType.isEmpty() ) {
0182         //debugFormula << "Manifest doesn't have media-type for" << path;
0183         return false;
0184     }
0185 
0186     const bool isOdf = mimeType.startsWith( "application/vnd.oasis.opendocument" );
0187     if ( !isOdf ) {
0188         tmpURL += "/maindoc.xml";
0189         //debugFormula << "tmpURL adjusted to" << tmpURL;
0190     }
0191 
0192     //debugFormula << "tmpURL=" << tmpURL;
0193 
0194     bool res = true;
0195     if ( tmpURL.startsWith( STORE_PROTOCOL )
0196          || tmpURL.startsWith( INTERNAL_PROTOCOL )
0197          || QUrl::fromUserInput(tmpURL).isRelative() )
0198     {
0199         if ( isOdf ) {
0200             store->pushDirectory();
0201             Q_ASSERT( tmpURL.startsWith( INTERNAL_PROTOCOL ) );
0202             QString relPath = QUrl::fromUserInput(tmpURL).path().mid( 1 );
0203             store->enterDirectory( relPath );
0204             res = m_document->loadOasisFromStore( store );
0205             store->popDirectory();
0206         } else {
0207             if ( tmpURL.startsWith( INTERNAL_PROTOCOL ) )
0208                 tmpURL = QUrl::fromUserInput(tmpURL).path().mid( 1 );
0209             res = m_document->loadFromStore( store, tmpURL );
0210         }
0211         m_document->setStoreInternal( true );
0212     }
0213     else {
0214         // Reference to an external document. Hmmm...
0215         m_document->setStoreInternal( false );
0216         QUrl url = QUrl::fromUserInput(tmpURL);
0217         if ( !url.isLocalFile() ) {
0218             //QApplication::restoreOverrideCursor();
0219 
0220             // For security reasons we need to ask confirmation if the
0221             // url is remote.
0222             int result = KMessageBox::warningYesNoCancel(
0223                 0, i18n( "This document contains an external link to a remote document\n%1", tmpURL ),
0224                 i18n( "Confirmation Required" ), KGuiItem( i18n( "Download" ) ), KGuiItem( i18n( "Skip" ) ) );
0225 
0226             if ( result == KMessageBox::Cancel ) {
0227                 //d->m_parent->setErrorMessage("USER_CANCELED");
0228                 return false;
0229             }
0230             if ( result == KMessageBox::Yes )
0231                 res = m_document->openUrl( url );
0232             // and if == No, res will still be false so we'll use a kounavail below
0233         }
0234         else
0235             res = m_document->openUrl( url );
0236     }
0237 
0238     if ( !res ) {
0239         QString errorMessage = m_document->errorMessage();
0240         return false;
0241     }
0242 
0243     tmpURL.clear();
0244 
0245     return res;
0246 }
0247 
0248 bool KoFormulaShape::loadOdfEmbedded( const KoXmlElement &topLevelElement,
0249                                       KoShapeLoadingContext &context )
0250 {
0251     Q_UNUSED(context);
0252     debugFormula << topLevelElement.nodeName();
0253 
0254 #if 0
0255     const KoXmlElement &topLevelElement = KoXml::namedItemNS(element, "http://www.w3.org/1998/Math/MathML", "math");
0256     if (topLevelElement.isNull()) {
0257         warnFormula << "no math element as first child";
0258         return false;
0259     }
0260 #endif
0261     // Create a new root element, load the formula and replace the old one.
0262     FormulaElement* formulaElement = new FormulaElement();
0263     formulaElement->readMathML( topLevelElement );
0264     delete m_formulaData->formulaElement();
0265     m_formulaData->setFormulaElement(formulaElement);
0266     m_formulaData->notifyDataChange(0, false);
0267 
0268     return true;
0269 }
0270 
0271 
0272 void KoFormulaShape::saveOdf( KoShapeSavingContext& context ) const
0273 {
0274     // FIXME: Add saving of embedded document if m_isInline is false;
0275 
0276     debugFormula << "Saving ODF in Formula";
0277     KoXmlWriter& writer = context.xmlWriter();
0278     writer.startElement("draw:frame");
0279     saveOdfAttributes(context, OdfAllAttributes);
0280     writer.startElement( "draw:object" );
0281     // TODO add some namespace magic to avoid adding "math:" namespace everywhere
0282     formulaData()->formulaElement()->writeMathML( &context.xmlWriter() );
0283     writer.endElement(); // draw:object
0284     writer.endElement(); // draw:frame
0285 }
0286 
0287 KoDocumentResourceManager *KoFormulaShape::resourceManager() const
0288 {
0289     return m_resourceManager;
0290 }