lib

KoDocumentChild.cpp
1/* This file is part of the KDE project
2 Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
3 Copyright (C) 2004-2006 David Faure <faure@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include "KoDocumentChild.h"
22#include "KoOasisStore.h"
23#include <KoDocument.h>
24#include <KoQueryTrader.h>
25#include <KoXmlWriter.h>
26#include <KoXmlNS.h>
27#include <KoUnit.h>
28#include <KoStore.h>
29#include <KoStoreDevice.h>
30
31#include <tdeparts/partmanager.h>
32
33#include <kmimetype.h>
34#include <tdelocale.h>
35#include <tdemessagebox.h>
36#include <kdebug.h>
37
38#include <tqapplication.h>
39
40#include <assert.h>
41
42// Define the protocol used here for embedded documents' URL
43// This used to "store" but KURL didn't like it,
44// so let's simply make it "tar" !
45#define STORE_PROTOCOL "tar"
46#define INTERNAL_PROTOCOL "intern"
47// Warning, keep it sync in koStore.cpp and koDocument.cpp
48
49/**********************************************************
50 *
51 * KoDocumentChild
52 *
53 **********************************************************/
54
55class KoDocumentChildPrivate
56{
57public:
58 KoDocumentChildPrivate()
59 {
60 }
61 ~KoDocumentChildPrivate()
62 {
63 }
64
65 KoDocument *m_parent;
66 KoDocument *m_doc;
67 bool m_deleted;
68};
69
70KoDocumentChild::KoDocumentChild( KoDocument* parent, KoDocument* doc, const TQRect& geometry )
71 : KoChild( parent )
72{
73 d = new KoDocumentChildPrivate;
74 d->m_parent = parent;
75 d->m_doc = doc;
76 setGeometry( geometry );
77 d->m_deleted = false;
78 if ( doc )
79 doc->setStoreInternal( !doc->hasExternURL() );
80}
81
82KoDocumentChild::KoDocumentChild( KoDocument* parent )
83 : KoChild( parent )
84{
85 d = new KoDocumentChildPrivate;
86 d->m_parent = parent;
87 d->m_doc = 0L;
88 d->m_deleted = false;
89}
90
92{
93 kdDebug()<<k_funcinfo<<"doc: "<<doc->url().url()<<endl;
94 d->m_doc = doc;
96
98}
99
101{
102 return d->m_doc;
103}
104
106{
107 return d->m_parent;
108}
109
110KoDocument* KoDocumentChild::hitTest( const TQPoint &p, const TQWMatrix &_matrix )
111{
112 if ( !region( _matrix ).contains( p ) || !d->m_doc )
113 return 0L;
114
115 TQWMatrix m( _matrix );
116 m = matrix() * m;
117 m.scale( xScaling(), yScaling() );
118
119 return d->m_doc->hitTest( p, m );
120}
121
122void KoDocumentChild::loadOasis( const TQDomElement &frameElement, const TQDomElement& objectElement )
123{
124 double x, y, w, h;
125 x = KoUnit::parseValue( frameElement.attributeNS( KoXmlNS::svg, "x", TQString() ) );
126 y = KoUnit::parseValue( frameElement.attributeNS( KoXmlNS::svg, "y", TQString() ) );
127 w = KoUnit::parseValue( frameElement.attributeNS( KoXmlNS::svg, "width", TQString() ) );
128 h = KoUnit::parseValue( frameElement.attributeNS( KoXmlNS::svg, "height", TQString() ) );
129 m_tmpGeometry = TQRect((int)x, (int)y, (int)w, (int)h); // #### double->int conversion
131
132 TQString url = objectElement.attributeNS( KoXmlNS::xlink, "href", TQString() );
133 if ( url[0] == '#' )
134 url = url.mid( 1 );
135 if ( url.startsWith( "./" ) )
136 m_tmpURL = TQString( INTERNAL_PROTOCOL ) + ":/" + url.mid( 2 );
137 else
138 m_tmpURL = url;
139 kdDebug() << k_funcinfo << m_tmpURL << endl;
140}
141
142
143bool KoDocumentChild::load( const TQDomElement& element, bool uppercase )
144{
145 if ( element.hasAttribute( "url" ) )
146 m_tmpURL = element.attribute("url");
147 if ( element.hasAttribute("mime") )
148 m_tmpMimeType = element.attribute("mime");
149
150 if ( m_tmpURL.isEmpty() )
151 {
152 kdDebug(30003) << "Empty 'url' attribute in OBJECT" << endl;
153 return false;
154 }
155 if ( m_tmpMimeType.isEmpty() )
156 {
157 kdDebug(30003) << "Empty 'mime' attribute in OBJECT" << endl;
158 return false;
159 }
160
161 bool brect = false;
162 TQDomNode n = element.firstChild();
163 for( ; !n.isNull(); n = n.nextSibling() )
164 {
165 TQDomElement e = n.toElement();
166 if ( e.isNull() ) continue;
167 if ( e.tagName() == "rect" || ( uppercase && e.tagName() == "RECT" ) )
168 {
169 brect = true;
170 int x, y, w, h;
171 x=y=w=h=0;
172 if ( e.hasAttribute( "x" ) )
173 x = e.attribute( "x" ).toInt(&brect);
174 if ( e.hasAttribute( "y" ) )
175 y = e.attribute( "y" ).toInt(&brect);
176 if ( e.hasAttribute( "w" ) )
177 w = e.attribute( "w" ).toInt(&brect);
178 if ( e.hasAttribute( "h" ) )
179 h = e.attribute( "h" ).toInt(&brect);
180 m_tmpGeometry = TQRect(x, y, w, h);
182 }
183 }
184
185 if ( !brect )
186 {
187 kdDebug(30003) << "Missing RECT in OBJECT" << endl;
188 return false;
189 }
190
191 return true;
192}
193
195{
196 assert( !m_tmpURL.isEmpty() );
197
198 kdDebug(30003) << "KoDocumentChild::loadDocument: trying to load " << m_tmpURL << endl;
199
200 // Backwards compatibility
201 if ( m_tmpMimeType == "application/x-killustrator" )
202 m_tmpMimeType = "application/x-kontour";
203
205 if ( e.isEmpty() )
206 {
207 kdWarning(30003) << "Could not create child document with type " << m_tmpMimeType << endl;
208 bool res = createUnavailDocument( store, true, m_tmpMimeType );
209 if ( res )
210 {
211 // Try to turn the mimetype name into its comment
212 TQString mimeName = m_tmpMimeType;
213 KMimeType::Ptr mime = KMimeType::mimeType( m_tmpMimeType );
214 if ( mime->name() != KMimeType::defaultMimeType() )
215 mimeName = mime->comment();
216 d->m_doc->setProperty( "unavailReason", i18n( "No handler found for %1" ).arg( mimeName ) );
217 }
218 return res;
219 }
220
221 return loadDocumentInternal( store, e, true /*open url*/, false /*not oasis*/ );
222}
223
224bool KoDocumentChild::loadOasisDocument( KoStore* store, const TQDomDocument& manifestDoc )
225{
226 TQString path = m_tmpURL;
227 if ( m_tmpURL.startsWith( INTERNAL_PROTOCOL ) ) {
228 path = store->currentDirectory();
229 if ( !path.isEmpty() )
230 path += '/';
231 TQString relPath = KURL( m_tmpURL ).path();
232 path += relPath.mid( 1 ); // remove leading '/'
233 }
234 if ( !path.endsWith( "/" ) )
235 path += '/';
236 const TQString mimeType = KoOasisStore::mimeForPath( manifestDoc, path );
237 kdDebug() << k_funcinfo << "path for manifest file=" << path << " mimeType=" << mimeType << endl;
238 if ( mimeType.isEmpty() ) {
239 kdError(30003) << "Manifest doesn't have media-type for " << path << endl;
240 return false;
241 }
242
244 if ( e.isEmpty() )
245 {
246 kdWarning(30003) << "Could not create child document with type " << mimeType << endl;
247 bool res = createUnavailDocument( store, true, mimeType );
248 if ( res )
249 {
250 // Try to turn the mimetype name into its comment
251 TQString mimeName = mimeType;
252 KMimeType::Ptr mime = KMimeType::mimeType( mimeType );
253 if ( mime->name() != KMimeType::defaultMimeType() )
254 mimeName = mime->comment();
255 d->m_doc->setProperty( "unavailReason", i18n( "No handler found for %1" ).arg( mimeName ) );
256 }
257 return res;
258 }
259
260 const bool oasis = mimeType.startsWith( "application/vnd.oasis.opendocument" );
261 if ( !oasis ) {
262 m_tmpURL += "/maindoc.xml";
263 kdDebug() << " m_tmpURL adjusted to " << m_tmpURL << endl;
264 }
265 return loadDocumentInternal( store, e, true /*open url*/, oasis );
266}
267
268bool KoDocumentChild::loadDocumentInternal( KoStore* store, const KoDocumentEntry& e, bool doOpenURL, bool oasis )
269{
270 kdDebug(30003) << "KoDocumentChild::loadDocumentInternal doOpenURL=" << doOpenURL << " m_tmpURL=" << m_tmpURL << endl;
271 KoDocument * doc = e.createDoc( d->m_parent );
272 if (!doc) {
273 kdWarning(30003) << "createDoc failed" << endl;
274 return false;
275 }
277
278 bool res = true;
279 if ( doOpenURL )
280 {
281 bool internalURL = false;
282 if ( m_tmpURL.startsWith( STORE_PROTOCOL ) || m_tmpURL.startsWith( INTERNAL_PROTOCOL ) || KURL::isRelativeURL( m_tmpURL ) )
283 {
284 if ( oasis ) {
285 store->pushDirectory();
286 assert( m_tmpURL.startsWith( INTERNAL_PROTOCOL ) );
287 TQString relPath = KURL( m_tmpURL ).path().mid( 1 );
288 store->enterDirectory( relPath );
289 res = d->m_doc->loadOasisFromStore( store );
290 store->popDirectory();
291 } else {
292 if ( m_tmpURL.startsWith( INTERNAL_PROTOCOL ) )
293 m_tmpURL = KURL( m_tmpURL ).path().mid( 1 );
294 res = d->m_doc->loadFromStore( store, m_tmpURL );
295 }
296 internalURL = true;
297 d->m_doc->setStoreInternal( true );
298 }
299 else
300 {
301 // Reference to an external document. Hmmm...
302 d->m_doc->setStoreInternal( false );
303 KURL url( m_tmpURL );
304 if ( !url.isLocalFile() )
305 {
306 TQApplication::restoreOverrideCursor();
307 // For security reasons we need to ask confirmation if the url is remote
309 0, i18n( "This document contains an external link to a remote document\n%1").arg(m_tmpURL),
310 i18n( "Confirmation Required" ), i18n( "Download" ), i18n( "Skip" ) );
311
312 if ( result == KMessageBox::Cancel )
313 {
314 d->m_parent->setErrorMessage("USER_CANCELED");
315 return false;
316 }
317 if ( result == KMessageBox::Yes )
318 res = d->m_doc->openURL( url );
319 // and if == No, res will still be false so we'll use a kounavail below
320 }
321 else
322 res = d->m_doc->openURL( url );
323 }
324 if ( !res )
325 {
326 // Keep the error message from the attempted loading
327 TQString errorMessage = d->m_doc->errorMessage();
328 delete d->m_doc;
329 d->m_doc = 0;
330 TQString tmpURL = m_tmpURL; // keep a copy, createUnavailDocument will erase it
331 // Not found -> use a kounavail instead
332 res = createUnavailDocument( store, false /* the URL doesn't exist, don't try to open it */, m_tmpMimeType );
333 if ( res )
334 {
335 d->m_doc->setProperty( "realURL", tmpURL ); // so that it gets saved correctly
336 d->m_doc->setStoreInternal( true );
337 if ( internalURL )
338 d->m_doc->setProperty( "unavailReason", i18n( "Could not load embedded object:\n%1" ).arg( errorMessage ) );
339 else
340 d->m_doc->setProperty( "unavailReason", i18n( "Could not load external document %1:\n%2" ).arg( tmpURL, errorMessage ) );
341 }
342 return res;
343 }
344 // Still waiting...
345 TQApplication::setOverrideCursor( waitCursor );
346 }
347
348 m_tmpURL = TQString();
349
350 // see KoDocument::insertChild for an explanation what's going on
351 // now :-)
352 if ( parentDocument() )
353 {
354 KoDocument *parent = parentDocument();
355
356 if ( parent->manager() && parent->manager()->parts() )
357 {
358 KParts::PartManager *manager = parent->manager();
359
360 if ( !manager->parts()->containsRef( d->m_doc ) &&
361 !parent->isSingleViewMode() )
362 manager->addPart( d->m_doc, false );
363 }
364 }
365
366 TQApplication::restoreOverrideCursor();
367
368 return res;
369}
370
371bool KoDocumentChild::createUnavailDocument( KoStore* store, bool doOpenURL, const TQString& mimeType )
372{
373 // We don't need a trader query here. We're looking for a very specific component.
374 KService::Ptr serv = KService::serviceByDesktopName( "kounavail" );
375 if ( serv == 0L )
376 {
377 kdWarning(30003) << "ERROR: service kounavail not found " << endl;
378 return false;
379 }
380 KoDocumentEntry e( serv );
381 if ( !loadDocumentInternal( store, e, doOpenURL, false ) )
382 return false;
383 d->m_doc->setProperty( "mimetype", mimeType );
384 return true;
385}
386
387bool KoDocumentChild::saveOasis( KoStore* store, KoXmlWriter* manifestWriter )
388{
389 TQString path;
390 if ( d->m_doc->isStoredExtern() )
391 {
392 kdDebug(30003)<<k_funcinfo<<" external (don't save) url:" << d->m_doc->url().url()<<endl;
393 path = d->m_doc->url().url();
394 }
395 else
396 {
397 // The name comes from KoDocumentChild (which was set while saving the
398 // parent document).
399 assert( d->m_doc->url().protocol() == INTERNAL_PROTOCOL );
400 const TQString name = d->m_doc->url().path();
401 kdDebug() << k_funcinfo << "saving " << name << endl;
402
403 if ( d->m_doc->nativeOasisMimeType().isEmpty() ) {
404 // Embedded object doesn't support OASIS OpenDocument, save in the old format.
405 kdDebug() << k_funcinfo << "Embedded object doesn't support OASIS OpenDocument, save in the old format." << endl;
406
407 if ( !d->m_doc->saveToStore( store, name ) )
408 return false;
409 }
410 else
411 {
412 // To make the children happy cd to the correct directory
413 store->pushDirectory();
414 store->enterDirectory( name );
415
416 if ( !d->m_doc->saveOasis( store, manifestWriter ) ) {
417 kdWarning(30003) << "KoDocumentChild::saveOasis failed" << endl;
418 return false;
419 }
420 // Now that we're done leave the directory again
421 store->popDirectory();
422 }
423
424 assert( d->m_doc->url().protocol() == INTERNAL_PROTOCOL );
425 path = store->currentDirectory();
426 if ( !path.isEmpty() )
427 path += '/';
428 path += d->m_doc->url().path();
429 if ( path.startsWith( "/" ) )
430 path = path.mid( 1 ); // remove leading '/', no wanted in manifest
431 }
432
433 // OOo uses a trailing slash for the path to embedded objects (== directories)
434 if ( !path.endsWith( "/" ) )
435 path += '/';
436 TQCString mimetype = d->m_doc->nativeOasisMimeType();
437 if ( mimetype.isEmpty() )
438 mimetype = d->m_doc->nativeFormatMimeType();
439 manifestWriter->addManifestEntry( path, mimetype );
440
441 return true;
442}
443
444void KoDocumentChild::saveOasisAttributes( KoXmlWriter &xmlWriter, const TQString& name )
445{
446 if ( !d->m_doc->isStoredExtern() )
447 {
448 // set URL in document so that KoDocument::saveChildrenOasis will save
449 // the actual embedded object with the right name in the store.
450 KURL u;
451 u.setProtocol( INTERNAL_PROTOCOL );
452 u.setPath( name );
453 kdDebug() << k_funcinfo << u << endl;
454 d->m_doc->setURL( u );
455 }
456
457 //<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"/>
458 xmlWriter.addAttribute( "xlink:type", "simple" );
459 xmlWriter.addAttribute( "xlink:show", "embed" );
460 xmlWriter.addAttribute( "xlink:actuate", "onLoad" );
461
462 const TQString ref = d->m_doc->isStoredExtern() ? d->m_doc->url().url() : "./"+ name;
463 kdDebug(30003) << "KoDocumentChild::saveOasis saving reference to embedded document as " << ref << endl;
464 xmlWriter.addAttribute( "xlink:href", /*"#" + */ref );
465}
466
467TQDomElement KoDocumentChild::save( TQDomDocument& doc, bool uppercase )
468{
469 if( d->m_doc )
470 {
471 TQDomElement e = doc.createElement( ( uppercase ? "OBJECT" : "object" ) );
472 if ( d->m_doc->url().protocol() != INTERNAL_PROTOCOL ) {
473 e.setAttribute( "url", d->m_doc->url().url() );
474 kdDebug() << "KoDocumentChild::save url=" << d->m_doc->url().url() << endl;
475 }
476 else {
477 e.setAttribute( "url", d->m_doc->url().path().mid( 1 ) );
478 kdDebug() << "KoDocumentChild::save url=" << d->m_doc->url().path().mid( 1 ) << endl;
479 }
480 e.setAttribute( "mime", d->m_doc->nativeFormatMimeType().data() );
481 kdDebug() << "KoDocumentChild::save mime=" << d->m_doc->nativeFormatMimeType() << endl;
482 TQDomElement rect = doc.createElement( ( uppercase ? "RECT" : "rect" ) );
483 rect.setAttribute( "x", geometry().left() );
484 rect.setAttribute( "y", geometry().top() );
485 rect.setAttribute( "w", geometry().width() );
486 rect.setAttribute( "h", geometry().height() );
487 e.appendChild(rect);
488 return e;
489 }
490 return TQDomElement();
491}
492
493bool KoDocumentChild::isStoredExtern() const
494{
495 assert( d->m_doc );
496 return d->m_doc->isStoredExtern();
497}
498
500{
501 return ( d->m_doc ? d->m_doc->url() : KURL() );
502}
503
504KoDocumentChild::~KoDocumentChild()
505{
506 if ( d->m_doc ) {
507 delete d->m_doc;
508 d->m_doc=0L;
509 }
510 delete d;
511}
512
514{
515 return d->m_deleted;
516}
517
518void KoDocumentChild::setDeleted( bool on )
519{
520 d->m_deleted = on;
521}
522
523#include "KoDocumentChild.moc"
static int warningYesNoCancel(TQWidget *parent, const TQString &text, const TQString &caption=TQString::null, const KGuiItem &buttonYes=KStdGuiItem::yes(), const KGuiItem &buttonNo=KStdGuiItem::no(), const TQString &dontAskAgainName=TQString::null, int options=Notify)
const TQPtrList< Part > * parts() const
virtual void addPart(Part *part, bool setActive=true)
PartManager * manager() const
KoChild is an abstract base class that represents the geometry associated with an embedded document.
Definition KoChild.h:42
void setGeometry(const TQRect &rect, bool noEmit=false)
Sets a new geometry for this child document.
Definition KoChild.cpp:72
virtual TQWMatrix matrix() const
Definition KoChild.cpp:291
virtual TQRegion region(const TQWMatrix &=TQWMatrix()) const
Definition KoChild.cpp:97
virtual void updateMatrix()
Stores the current transformation of this child into a matrix.
Definition KoChild.cpp:275
TQRect geometry() const
Definition KoChild.cpp:92
virtual double xScaling() const
Definition KoChild.cpp:149
virtual double yScaling() const
Definition KoChild.cpp:154
TQRect m_tmpGeometry
This variable is set after parsing the OBJECT tag in load and is reset after calling loadDocument.
virtual bool saveOasis(KoStore *store, KoXmlWriter *manifestWriter)
Save an embedded object to an OASIS store.
virtual bool load(const TQDomElement &element, bool uppercase=false)
Parses the "object" tag.
KoDocument * document() const
virtual bool loadDocument(KoStore *)
Actually loads the document from the disk/net or from the store, depending on url.
void saveOasisAttributes(KoXmlWriter &xmlWriter, const TQString &name)
Save an embedded object to OASIS.
bool isDeleted() const
This document (child) is deleted.
virtual TQDomElement save(TQDomDocument &doc, bool uppercase=false)
Writes the "object" tag, but does NOT write the content of the embedded documents.
virtual void setDocument(KoDocument *doc, const TQRect &geometry)
Call this function only directly after calling the constructor that takes only a parent as argument.
TQString m_tmpMimeType
This variable is set after parsing the OBJECT tag in load and is reset after calling loadDocument.
TQString m_tmpURL
Holds the source of this object, for example "file:/home/weis/image.gif" or "tar:/table1/2" if it is ...
virtual KoDocument * parentDocument() const
virtual bool loadOasisDocument(KoStore *store, const TQDomDocument &manifestDoc)
Actually loads the document from the disk/net or from the store depending on the document's url.
Represents an available KOffice component that supports the document interface.
static KoDocumentEntry queryByMimeType(const TQString &mimetype)
This is a convenience function.
bool isEmpty() const
KoDocument * createDoc(KoDocument *parent=0, const char *name=0) const
Uses the factory of the component to create a document.
The KOffice document class.
Definition KoDocument.h:61
virtual bool isStoredExtern() const
Return true if url() is a real filename, false if url() is an internal url in the store,...
virtual KParts::Part * hitTest(TQWidget *widget, const TQPoint &globalPos)
Reimplemented from KParts::Part.
bool isSingleViewMode() const
Tells whether this document is in singleview mode.
void setStoreInternal(bool i)
Set when you want an external embedded document to be stored internally.
static TQString mimeForPath(const TQDomDocument &doc, const TQString &fullPath)
Another method for loading: get mimetype from full path, using the manifest.
Saves and loads KOffice documents using various backends.
Definition KoStore.h:40
void pushDirectory()
Stacks the current directory.
Definition KoStore.cpp:416
void popDirectory()
Restores the previously pushed directory.
Definition KoStore.cpp:421
bool enterDirectory(const TQString &directory)
Enters one or multiple directories.
Definition KoStore.cpp:373
TQString currentDirectory() const
Returns the current directory.
Definition KoStore.cpp:399
static double parseValue(TQString value, double defaultVal=0.0)
parse common KOffice and OO values, like "10cm", "5mm" to pt
Definition KoUnit.cpp:148
A class for writing out XML (to any TQIODevice), with a special attention on performance.
Definition KoXmlWriter.h:37
void addManifestEntry(const TQString &fullPath, const TQString &mediaType)
Special helper for writing "manifest" files This is equivalent to startElement/2*addAttribute/endElem...
void addAttribute(const char *attrName, const TQString &value)
Overloaded version of addAttribute( const char*, const char* ), which is a bit slower because it need...
Definition KoXmlWriter.h:76