00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
00043
00044
00045 #define STORE_PROTOCOL "tar"
00046 #define INTERNAL_PROTOCOL "intern"
00047
00048
00049
00050
00051
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);
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
00201 if ( m_tmpMimeType == "application/x-killustrator" )
00202 m_tmpMimeType = "application/x-kontour";
00203
00204 return createAndLoadDocument( store, true , false , 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 );
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 , 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
00246 QString mimeName = mimeType;
00247 KMimeType::Ptr mime = KMimeType::mimeType( mimeType );
00248 if ( mime )
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
00296 d->m_doc->setStoreInternal( false );
00297 KUrl url( m_tmpURL );
00298 if ( !url.isLocalFile() )
00299 {
00300 QApplication::restoreOverrideCursor();
00301
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
00314 }
00315 else
00316 res = d->m_doc->openURL( url );
00317 }
00318 if ( !res )
00319 {
00320
00321 QString errorMessage = d->m_doc->errorMessage();
00322 delete d->m_doc;
00323 d->m_doc = 0;
00324 QString tmpURL = m_tmpURL;
00325
00326 res = createUnavailDocument( store, false , m_tmpMimeType );
00327 if ( res )
00328 {
00329 d->m_doc->setProperty( "realURL", tmpURL );
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
00339 QApplication::setOverrideCursor( Qt::WaitCursor );
00340 }
00341
00342 m_tmpURL = QString();
00343
00344
00345
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
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
00397
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
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
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
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 );
00430 }
00431
00432
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
00448
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
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"