F:/KPlato/koffice/libs/kofficecore/KoDocumentChild.cpp

Aller à la documentation de ce fichier.
00001 /* This file is part of the KDE project
00002    Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
00003    Copyright (C) 2004-2006 David Faure <faure@kde.org>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "KoDocumentChild.h"
00022 #include "KoOasisStore.h"
00023 #include <KoDocument.h>
00024 #include <KoQueryTrader.h>
00025 #include <KoXmlReader.h>
00026 #include <KoXmlWriter.h>
00027 #include <KoXmlNS.h>
00028 #include <KoUnit.h>
00029 #include <KoStore.h>
00030 #include <KoStoreDevice.h>
00031 
00032 #include <kparts/partmanager.h>
00033 
00034 #include <kmimetype.h>
00035 #include <klocale.h>
00036 #include <kmessagebox.h>
00037 #include <kdebug.h>
00038 
00039 #include <QApplication>
00040 #include <assert.h>
00041 
00042 // Define the protocol used here for embedded documents' URL
00043 // This used to "store" but KUrl didn't like it,
00044 // so let's simply make it "tar" !
00045 #define STORE_PROTOCOL "tar"
00046 #define INTERNAL_PROTOCOL "intern"
00047 // Warning, keep it sync in koStore.cc and koDocument.cc
00048 
00049 /**********************************************************
00050  *
00051  * KoDocumentChild
00052  *
00053  **********************************************************/
00054 
00055 class KoDocumentChildPrivate
00056 {
00057 public:
00058   KoDocumentChildPrivate()
00059   {
00060   }
00061   ~KoDocumentChildPrivate()
00062   {
00063   }
00064 
00065   KoDocument *m_parent;
00066   KoDocument *m_doc;
00067   bool m_deleted;
00068 };
00069 
00070 KoDocumentChild::KoDocumentChild( KoDocument* parent, KoDocument* doc, const QRect& geometry )
00071     : KoChild( parent )
00072 {
00073   d = new KoDocumentChildPrivate;
00074   d->m_parent = parent;
00075   d->m_doc = doc;
00076   setGeometry( geometry );
00077   d->m_deleted = false;
00078   if ( doc )
00079     doc->setStoreInternal( !doc->hasExternURL() );
00080 }
00081 
00082 KoDocumentChild::KoDocumentChild( KoDocument* parent )
00083     : KoChild( parent )
00084 {
00085   d = new KoDocumentChildPrivate;
00086   d->m_parent = parent;
00087   d->m_doc = 0L;
00088   d->m_deleted = false;
00089 }
00090 
00091 void KoDocumentChild::setDocument( KoDocument *doc, const QRect &geometry )
00092 {
00093   kDebug()<<k_funcinfo<<"doc: "<<doc->url().url()<<endl;
00094   d->m_doc = doc;
00095   setGeometry( geometry );
00096 
00097   updateMatrix();
00098 }
00099 
00100 KoDocument *KoDocumentChild::document() const
00101 {
00102   return d->m_doc;
00103 }
00104 
00105 KoDocument* KoDocumentChild::parentDocument() const
00106 {
00107   return d->m_parent;
00108 }
00109 
00110 KoDocument* KoDocumentChild::hitTest( const QPoint& p, KoView* view, const QMatrix& _matrix )
00111 {
00112   if ( !region( _matrix ).contains( p ) || !d->m_doc )
00113     return 0L;
00114 
00115   QMatrix m( _matrix );
00116   m = matrix() * m;
00117   m.scale( xScaling(), yScaling() );
00118 
00119   return d->m_doc->hitTest( p, view, m );
00120 }
00121 
00122 void KoDocumentChild::loadOasis( const KoXmlElement &frameElement, const KoXmlElement& objectElement )
00123 {
00124     double x, y, w, h;
00125     x = KoUnit::parseValue( frameElement.attributeNS( KoXmlNS::svg, "x", QString::null ) );
00126     y = KoUnit::parseValue( frameElement.attributeNS( KoXmlNS::svg, "y", QString::null ) );
00127     w = KoUnit::parseValue( frameElement.attributeNS( KoXmlNS::svg, "width", QString::null ) );
00128     h = KoUnit::parseValue( frameElement.attributeNS( KoXmlNS::svg, "height", QString::null ) );
00129     m_tmpGeometry = QRect((int)x, (int)y, (int)w, (int)h); // #### double->int conversion
00130     setGeometry(m_tmpGeometry);
00131 
00132     QString url = objectElement.attributeNS( KoXmlNS::xlink, "href", QString::null );
00133     if ( url[0] == '#' )
00134         url = url.mid( 1 );
00135     if ( url.startsWith( "./" ) )
00136         m_tmpURL = QString( INTERNAL_PROTOCOL ) + ":/" + url.mid( 2 );
00137     else
00138         m_tmpURL = url;
00139     kDebug() << k_funcinfo << m_tmpURL << endl;
00140 }
00141 
00142 
00143 bool KoDocumentChild::load( const KoXmlElement& element, bool uppercase )
00144 {
00145     if ( element.hasAttribute( "url" ) )
00146         m_tmpURL = element.attribute("url");
00147     if ( element.hasAttribute("mime") )
00148         m_tmpMimeType = element.attribute("mime");
00149 
00150     if ( m_tmpURL.isEmpty() )
00151     {
00152         kDebug(30003) << "Empty 'url' attribute in OBJECT" << endl;
00153         return false;
00154     }
00155     if ( m_tmpMimeType.isEmpty() )
00156     {
00157         kDebug(30003) << "Empty 'mime' attribute in OBJECT" << endl;
00158         return false;
00159     }
00160 
00161     bool brect = false;
00162     KoXmlNode n = element.firstChild();
00163     for( ; !n.isNull(); n = n.nextSibling() )
00164     {
00165         KoXmlElement e = n.toElement();
00166         if ( e.isNull() ) continue;
00167         if ( e.tagName() == "rect" || ( uppercase && e.tagName() == "RECT" ) )
00168         {
00169             brect = true;
00170             int x, y, w, h;
00171             x=y=w=h=0;
00172             if ( e.hasAttribute( "x" ) )
00173                 x = e.attribute( "x" ).toInt(&brect);
00174             if ( e.hasAttribute( "y" ) )
00175                 y = e.attribute( "y" ).toInt(&brect);
00176             if ( e.hasAttribute( "w" ) )
00177                 w = e.attribute( "w" ).toInt(&brect);
00178             if ( e.hasAttribute( "h" ) )
00179                 h = e.attribute( "h" ).toInt(&brect);
00180             m_tmpGeometry = QRect(x, y, w, h);
00181             setGeometry(m_tmpGeometry);
00182         }
00183     }
00184 
00185     if ( !brect )
00186     {
00187         kDebug(30003) << "Missing RECT in OBJECT" << endl;
00188         return false;
00189     }
00190 
00191     return true;
00192 }
00193 
00194 bool KoDocumentChild::loadDocument( KoStore* store )
00195 {
00196     assert( !m_tmpURL.isEmpty() );
00197 
00198     kDebug(30003) << "KoDocumentChild::loadDocument: trying to load " << m_tmpURL << endl;
00199 
00200     // Backwards compatibility
00201     if ( m_tmpMimeType == "application/x-killustrator" )
00202         m_tmpMimeType = "application/x-kontour";
00203 
00204     return createAndLoadDocument( store, true /*open url*/, false /*not oasis*/, m_tmpMimeType );
00205 }
00206 
00207 bool KoDocumentChild::loadOasisDocument( KoStore* store, const KoXmlDocument& manifestDoc )
00208 {
00209     QString path = m_tmpURL;
00210     if ( m_tmpURL.startsWith( INTERNAL_PROTOCOL ) ) {
00211         path = store->currentDirectory();
00212         if ( !path.isEmpty() )
00213             path += '/';
00214         QString relPath = KUrl( m_tmpURL ).path();
00215         path += relPath.mid( 1 ); // remove leading '/'
00216     }
00217     if ( !path.endsWith( "/" ) )
00218         path += '/';
00219     const QString mimeType = KoOasisStore::mimeForPath( manifestDoc, path );
00220     kDebug() << k_funcinfo << "path for manifest file=" << path << " mimeType=" << mimeType << endl;
00221     if ( mimeType.isEmpty() ) {
00222         kError(30003) << "Manifest doesn't have media-type for " << path << endl;
00223         return false;
00224     }
00225 
00226     const bool oasis = mimeType.startsWith( "application/vnd.oasis.opendocument" );
00227     if ( !oasis ) {
00228         m_tmpURL += "/maindoc.xml";
00229         kDebug() << " m_tmpURL adjusted to " << m_tmpURL << endl;
00230     }
00231     return createAndLoadDocument( store, true /*open url*/, oasis, mimeType );
00232 }
00233 
00234 bool KoDocumentChild::createAndLoadDocument( KoStore* store, bool doOpenURL, bool oasis, const QString& mimeType )
00235 {
00236     kDebug(30003) << "KoDocumentChild::loadDocumentInternal doOpenURL=" << doOpenURL << " m_tmpURL=" << m_tmpURL << endl;
00237     QString errorMsg;
00238     KoDocumentEntry e = KoDocumentEntry::queryByMimeType( mimeType );
00239     if ( e.isEmpty() )
00240     {
00241         kWarning(30003) << "Could not create child document with type " << mimeType << endl;
00242         bool res = createUnavailDocument( store, true, mimeType );
00243         if ( res )
00244         {
00245             // Try to turn the mimetype name into its comment
00246             QString mimeName = mimeType;
00247             KMimeType::Ptr mime = KMimeType::mimeType( mimeType );
00248             if ( mime /*mime->name() != KMimeType::defaultMimeType()*/ )
00249                 mimeName = mime->comment();
00250             d->m_doc->setProperty( "unavailReason", i18n( "No handler found for %1", mimeName ) );
00251         }
00252         return res;
00253     }
00254 
00255     KoDocument * doc = e.createDoc( &errorMsg, d->m_parent );
00256     if (!doc) {
00257         kWarning(30003) << "createDoc failed" << endl;
00258         bool res = createUnavailDocument( store, true, mimeType );
00259         if ( res )
00260         {
00261             d->m_doc->setProperty( "unavailReason", i18n( "Error loading %1:\n%2", e.service()->library(), errorMsg ) );
00262     }
00263         return res;
00264     }
00265     return finishLoadingDocument( store, doc, doOpenURL, oasis );
00266 }
00267 
00268 bool KoDocumentChild::finishLoadingDocument( KoStore* store, KoDocument* doc, bool doOpenURL, bool oasis )
00269 {
00270     setDocument( doc, m_tmpGeometry );
00271 
00272     bool res = true;
00273     if ( doOpenURL )
00274     {
00275         bool internalURL = false;
00276         if ( m_tmpURL.startsWith( STORE_PROTOCOL ) || m_tmpURL.startsWith( INTERNAL_PROTOCOL ) || KUrl::isRelativeUrl( m_tmpURL ) )
00277         {
00278             if ( oasis ) {
00279                 store->pushDirectory();
00280                 assert( m_tmpURL.startsWith( INTERNAL_PROTOCOL ) );
00281                 QString relPath = KUrl( m_tmpURL ).path().mid( 1 );
00282                 store->enterDirectory( relPath );
00283                 res = d->m_doc->loadOasisFromStore( store );
00284                 store->popDirectory();
00285             } else {
00286                 if ( m_tmpURL.startsWith( INTERNAL_PROTOCOL ) )
00287                     m_tmpURL = KUrl( m_tmpURL ).path().mid( 1 );
00288                 res = d->m_doc->loadFromStore( store, m_tmpURL );
00289             }
00290             internalURL = true;
00291             d->m_doc->setStoreInternal( true );
00292         }
00293         else
00294         {
00295             // Reference to an external document. Hmmm...
00296             d->m_doc->setStoreInternal( false );
00297             KUrl url( m_tmpURL );
00298             if ( !url.isLocalFile() )
00299             {
00300                 QApplication::restoreOverrideCursor();
00301                 // For security reasons we need to ask confirmation if the url is remote
00302                 int result = KMessageBox::warningYesNoCancel(
00303                     0, i18n( "This document contains an external link to a remote document\n%1", m_tmpURL),
00304                     i18n( "Confirmation Required" ), KGuiItem(i18n( "Download" )), KGuiItem(i18n( "Skip" ) ));
00305 
00306                 if ( result == KMessageBox::Cancel )
00307                 {
00308                     d->m_parent->setErrorMessage("USER_CANCELED");
00309                     return false;
00310                 }
00311                 if ( result == KMessageBox::Yes )
00312                     res = d->m_doc->openURL( url );
00313                 // and if == No, res will still be false so we'll use a kounavail below
00314             }
00315             else
00316                 res = d->m_doc->openURL( url );
00317         }
00318         if ( !res )
00319         {
00320             // Keep the error message from the attempted loading
00321             QString errorMessage = d->m_doc->errorMessage();
00322             delete d->m_doc;
00323             d->m_doc = 0;
00324             QString tmpURL = m_tmpURL; // keep a copy, createUnavailDocument will erase it
00325             // Not found -> use a kounavail instead
00326             res = createUnavailDocument( store, false /* the URL doesn't exist, don't try to open it */, m_tmpMimeType );
00327             if ( res )
00328             {
00329                 d->m_doc->setProperty( "realURL", tmpURL ); // so that it gets saved correctly
00330                 d->m_doc->setStoreInternal( true );
00331                 if ( internalURL )
00332                     d->m_doc->setProperty( "unavailReason", i18n( "Could not load embedded object:\n%1", errorMessage ) );
00333                 else
00334                     d->m_doc->setProperty( "unavailReason", i18n( "Could not load external document %1:\n%2", tmpURL, errorMessage ) );
00335             }
00336             return res;
00337         }
00338         // Still waiting...
00339         QApplication::setOverrideCursor( Qt::WaitCursor );
00340     }
00341 
00342     m_tmpURL = QString();
00343 
00344     // see KoDocument::insertChild for an explanation what's going on
00345     // now :-)
00346     if ( parentDocument() )
00347     {
00348         KoDocument *parent = parentDocument();
00349 
00350         KParts::PartManager* manager = parent->manager();
00351         if ( manager && !manager->parts().isEmpty() )
00352         {
00353             if ( !manager->parts().contains( d->m_doc ) &&
00354                  !parent->isSingleViewMode() )
00355                 manager->addPart( d->m_doc, false );
00356         }
00357     }
00358 
00359     QApplication::restoreOverrideCursor();
00360 
00361     return res;
00362 }
00363 
00364 bool KoDocumentChild::createUnavailDocument( KoStore* store, bool doOpenURL, const QString& mimeType )
00365 {
00366     // We don't need a trader query here. We're looking for a very specific component.
00367     KService::Ptr serv = KService::serviceByDesktopName( "kounavail" );
00368     if ( serv.isNull() )
00369     {
00370         kWarning(30003) << "ERROR: service kounavail not found " << endl;
00371         return false;
00372     }
00373     KoDocumentEntry e( serv );
00374     if ( e.isEmpty() )
00375         return false;
00376     QString errorMsg;
00377     KoDocument * doc = e.createDoc( &errorMsg, d->m_parent );
00378     if ( !doc )
00379         return false;
00380     if ( !finishLoadingDocument( store, doc, doOpenURL, false ) )
00381         return false;
00382     d->m_doc->setProperty( "mimetype", mimeType );
00383     return true;
00384 }
00385 
00386 bool KoDocumentChild::saveOasis( KoStore* store, KoXmlWriter* manifestWriter )
00387 {
00388     QString path;
00389     if ( d->m_doc->isStoredExtern() )
00390     {
00391         kDebug(30003)<<k_funcinfo<<" external (don't save) url:" << d->m_doc->url().url()<<endl;
00392         path = d->m_doc->url().url();
00393     }
00394     else
00395     {
00396         // The name comes from KoDocumentChild (which was set while saving the
00397         // parent document).
00398         assert( d->m_doc->url().protocol() == INTERNAL_PROTOCOL );
00399         const QString name = d->m_doc->url().path();
00400         kDebug() << k_funcinfo << "saving " << name << endl;
00401 
00402         if ( d->m_doc->nativeOasisMimeType().isEmpty() ) {
00403             // Embedded object doesn't support OASIS OpenDocument, save in the old format.
00404             kDebug() << k_funcinfo << "Embedded object doesn't support OASIS OpenDocument, save in the old format." << endl;
00405 
00406             if ( !d->m_doc->saveToStore( store, name ) )
00407                 return false;
00408         }
00409         else
00410         {
00411             // To make the children happy cd to the correct directory
00412             store->pushDirectory();
00413             store->enterDirectory( name );
00414 
00415             if ( !d->m_doc->saveOasis( store, manifestWriter ) ) {
00416                 kWarning(30003) << "KoDocumentChild::saveOasis failed" << endl;
00417                 return false;
00418             }
00419             // Now that we're done leave the directory again
00420             store->popDirectory();
00421         }
00422 
00423         assert( d->m_doc->url().protocol() == INTERNAL_PROTOCOL );
00424         path = store->currentDirectory();
00425         if ( !path.isEmpty() )
00426             path += '/';
00427         path += d->m_doc->url().path();
00428         if ( path.startsWith( "/" ) )
00429             path = path.mid( 1 ); // remove leading '/', no wanted in manifest
00430     }
00431 
00432     // OOo uses a trailing slash for the path to embedded objects (== directories)
00433     if ( !path.endsWith( "/" ) )
00434         path += '/';
00435     QByteArray mimetype = d->m_doc->nativeOasisMimeType();
00436     if ( mimetype.isEmpty() )
00437         mimetype = d->m_doc->nativeFormatMimeType();
00438     manifestWriter->addManifestEntry( path, mimetype );
00439 
00440     return true;
00441 }
00442 
00443 void KoDocumentChild::saveOasisAttributes( KoXmlWriter &xmlWriter, const QString& name )
00444 {
00445     if ( !d->m_doc->isStoredExtern() )
00446     {
00447         // set URL in document so that KoDocument::saveChildrenOasis will save
00448         // the actual embedded object with the right name in the store.
00449         KUrl u;
00450         u.setProtocol( INTERNAL_PROTOCOL );
00451         u.setPath( name );
00452         kDebug() << k_funcinfo << u << endl;
00453         d->m_doc->setURL( u );
00454     }
00455 
00456     //<draw:object draw:style-name="standard" draw:id="1" draw:layer="layout" svg:width="14.973cm" svg:height="4.478cm" svg:x="11.641cm" svg:y="14.613cm" xlink:href="#./Object 1" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/>
00457     xmlWriter.addAttribute( "xlink:type", "simple" );
00458     xmlWriter.addAttribute( "xlink:show", "embed" );
00459     xmlWriter.addAttribute( "xlink:actuate", "onLoad" );
00460 
00461     const QString ref = d->m_doc->isStoredExtern() ? d->m_doc->url().url() : "./"+ name;
00462     kDebug(30003) << "KoDocumentChild::saveOasis saving reference to embedded document as " << ref << endl;
00463     xmlWriter.addAttribute( "xlink:href", /*"#" + */ref );
00464 }
00465 
00466 QDomElement KoDocumentChild::save( QDomDocument& doc, bool uppercase )
00467 {
00468     if( d->m_doc )
00469     {
00470         QDomElement e = doc.createElement( ( uppercase ? "OBJECT" : "object" ) );
00471         if ( d->m_doc->url().protocol() != INTERNAL_PROTOCOL ) {
00472             e.setAttribute( "url", d->m_doc->url().url() );
00473             kDebug() << "KoDocumentChild::save url=" << d->m_doc->url().url() << endl;
00474         }
00475         else {
00476             e.setAttribute( "url", d->m_doc->url().path().mid( 1 ) );
00477             kDebug() << "KoDocumentChild::save url=" << d->m_doc->url().path().mid( 1 ) << endl;
00478         }
00479         e.setAttribute( "mime", QString( d->m_doc->nativeFormatMimeType() ) );
00480         kDebug() << "KoDocumentChild::save mime=" << d->m_doc->nativeFormatMimeType() << endl;
00481         QDomElement rect = doc.createElement( ( uppercase ? "RECT" : "rect" ) );
00482         rect.setAttribute( "x", geometry().left() );
00483         rect.setAttribute( "y", geometry().top() );
00484         rect.setAttribute( "w", geometry().width() );
00485         rect.setAttribute( "h", geometry().height() );
00486         e.appendChild(rect);
00487         return e;
00488     }
00489     return QDomElement();
00490 }
00491 
00492 bool KoDocumentChild::isStoredExtern() const
00493 {
00494     assert( d->m_doc );
00495     return d->m_doc->isStoredExtern();
00496 }
00497 
00498 KUrl KoDocumentChild::url() const
00499 {
00500     return ( d->m_doc ? d->m_doc->url() : KUrl() );
00501 }
00502 
00503 KoDocumentChild::~KoDocumentChild()
00504 {
00505   if ( d->m_doc ) {
00506     delete d->m_doc;
00507     d->m_doc=0L;
00508   }
00509   delete d;
00510 }
00511 
00512 bool KoDocumentChild::isDeleted() const
00513 {
00514     return d->m_deleted;
00515 }
00516 
00517 void KoDocumentChild::setDeleted( bool on )
00518 {
00519     d->m_deleted = on;
00520 }
00521 
00522 #include "KoDocumentChild.moc"

Généré le Wed Nov 22 23:41:02 2006 pour KPlato par  doxygen 1.5.1-p1