F:/KPlato/koffice/libs/kofficecore/KoDocument.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) 2000-2005 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 "KoDocument.h"
00022 
00023 #include "KoDom.h"
00024 #include "KoDocument_p.h"
00025 #include "KoDocumentAdaptor.h"
00026 #include <QtDBus/QtDBus>
00027 #include "KoDocumentChild.h"
00028 #include "KoView.h"
00029 #include "KoMainWindow.h"
00030 #include "KoFilterManager.h"
00031 #include "KoDocumentInfo.h"
00032 #include "KoOasisStyles.h"
00033 #include "KoOasisStore.h"
00034 #include "KoXmlNS.h"
00035 #include "KoOpenPane.h"
00036 
00037 #include <KoStoreDevice.h>
00038 #include <KoXmlWriter.h>
00039 
00040 #include <kapplication.h>
00041 #include <kdialog.h>
00042 #include <kdebug.h>
00043 #include <kdeversion.h>
00044 #include <kfileitem.h>
00045 #include <kiconloader.h>
00046 #include <kio/job.h>
00047 #include <kio/jobuidelegate.h>
00048 #include <kio/netaccess.h>
00049 #include <klocale.h>
00050 #include <kmessagebox.h>
00051 #include <kmimetype.h>
00052 #include <kparts/partmanager.h>
00053 #include <kprinter.h>
00054 #include <ksavefile.h>
00055 #include <kxmlguifactory.h>
00056 
00057 #include <QBuffer>
00058 #include <QCursor>
00059 #include <QDir>
00060 #include <QFile>
00061 #include <QFileInfo>
00062 #include <QImage>
00063 #include <QMap>
00064 #include <QPainter>
00065 #include <QTimer>
00066 #include <QXmlSimpleReader>
00067 #include <QLayout>
00068 #include <QByteArray>
00069 #include <QPixmap>
00070 #include <QChildEvent>
00071 #include <Q3PtrList>
00072 #include <QEvent>
00073 #include <QResizeEvent>
00074 #include <QDateTime>
00075 #include <config.h>
00076 #include <assert.h>
00077 
00078 
00079 // Define the protocol used here for embedded documents' URL
00080 // This used to "store" but KUrl didn't like it,
00081 // so let's simply make it "tar" !
00082 #define STORE_PROTOCOL "tar"
00083 // The internal path is a hack to make KUrl happy and still pass
00084 // some kind of relative path to KoDocumentChild
00085 #define INTERNAL_PROTOCOL "intern"
00086 #define INTERNAL_PREFIX "intern:/"
00087 // Warning, keep it sync in koStore.cc and koDocumentChild.cc
00088 
00089 Q3PtrList<KoDocument> *KoDocument::s_documentList=0L;
00090 
00091 using namespace std;
00092 class KoViewWrapperWidget;
00093 
00094 /**********************************************************
00095  *
00096  * KoDocument
00097  *
00098  **********************************************************/
00099 
00100 const int KoDocument::s_defaultAutoSave = 300; // 5 minutes
00101 
00102 
00103 //static
00104 QString KoDocument::newObjectName()
00105 {
00106     static int s_docIFNumber = 0;
00107     QString name; name.setNum( s_docIFNumber++ ); name.prepend("document_");
00108     return name;
00109 }
00110 
00111 class KoDocument::Private
00112 {
00113 public:
00114     Private() :
00115 //         m_dcopObject( 0L ),
00116         filterManager( 0L ),
00117         m_specialOutputFlag( 0 ), // default is native format
00118         m_isImporting( false ), m_isExporting( false ),
00119         m_numOperations( 0 ),
00120         modifiedAfterAutosave( false ),
00121         m_autosaving( false ),
00122         m_shouldCheckAutoSaveFile( true ),
00123         m_autoErrorHandlingEnabled( true ),
00124         m_backupFile( true ),
00125         m_backupPath( QString::null ),
00126         m_doNotSaveExtDoc( false ),
00127         m_current( false ),
00128         m_storeInternal( false ),
00129         m_bLoading( false ),
00130         m_startUpWidget( 0 )
00131     {
00132         m_confirmNonNativeSave[0] = true;
00133         m_confirmNonNativeSave[1] = true;
00134         if ( KGlobal::locale()->measureSystem() == KLocale::Imperial ) {
00135             m_unit = KoUnit::U_INCH;
00136         } else {
00137             m_unit = KoUnit::U_CM;
00138         }
00139     }
00140 
00141     QList<KoView*> m_views;
00142     Q3PtrList<KoDocumentChild> m_children;
00143     Q3PtrList<KoMainWindow> m_shells;
00144     QList<QDomDocument> m_viewBuildDocuments;
00145 
00146     KoViewWrapperWidget *m_wrapperWidget;
00147 //     KoDocumentIface * m_dcopObject;
00148     KoDocumentInfo *m_docInfo;
00149 
00150     KoUnit::Unit m_unit;
00151 
00152     KoFilterManager * filterManager; // The filter-manager to use when loading/saving [for the options]
00153 
00154     QByteArray mimeType; // The actual mimetype of the document
00155     QByteArray outputMimeType; // The mimetype to use when saving
00156     bool m_confirmNonNativeSave [2]; // used to pop up a dialog when saving for the
00157                                      // first time if the file is in a foreign format
00158                                      // (Save/Save As, Export)
00159     int m_specialOutputFlag; // See KoFileDialog in koMainWindow.cc
00160     bool m_isImporting, m_isExporting; // File --> Import/Export vs File --> Open/Save
00161 
00162     QTimer m_autoSaveTimer;
00163     QString lastErrorMessage; // see openFile()
00164     int m_autoSaveDelay; // in seconds, 0 to disable.
00165     int m_numOperations;
00166     bool modifiedAfterAutosave;
00167     bool m_bSingleViewMode;
00168     bool m_autosaving;
00169     bool m_shouldCheckAutoSaveFile; // usually true
00170     bool m_autoErrorHandlingEnabled; // usually true
00171     bool m_backupFile;
00172     QString m_backupPath;
00173     bool m_doNotSaveExtDoc; // makes it possible to save only internally stored child documents
00174     bool m_current;
00175     bool m_storeInternal; // Store this doc internally even if url is external
00176     bool m_bLoading; // True while loading (openURL is async)
00177 
00178     KoOpenPane* m_startUpWidget;
00179     QString m_templateType;
00180     QList<KoVersionInfo> m_versionInfo;
00181 };
00182 
00183 // Used in singleViewMode
00184 class KoViewWrapperWidget : public QWidget
00185 {
00186 public:
00187     KoViewWrapperWidget( QWidget *parent )
00188         : QWidget( parent )
00189     {
00190         KGlobal::locale()->insertCatalog("koffice");
00191         // Tell the iconloader about share/apps/koffice/icons
00192         KGlobal::iconLoader()->addAppDir("koffice");
00193         m_view = 0L;
00194         // Avoid warning from KParts - we'll have the KoView as focus proxy anyway
00195         setFocusPolicy( Qt::ClickFocus );
00196     }
00197 
00198     virtual ~KoViewWrapperWidget() {
00199         setFocusProxy( 0 ); // to prevent a crash due to clearFocus (#53466)
00200     }
00201 
00202     virtual void resizeEvent( QResizeEvent * )
00203     {
00204         QWidget *wid = findChild<QWidget *>( "" );
00205         wid->setGeometry( 0, 0, width(), height() );
00206     }
00207 
00208     virtual void childEvent( QChildEvent *ev )
00209     {
00210         if ( ev->type() == QEvent::ChildAdded )
00211             resizeEvent( 0L );
00212     }
00213 
00214     // Called by openFile()
00215     void setKoView( KoView * view ) {
00216         m_view = view;
00217         setFocusProxy( m_view );
00218     }
00219     KoView * koView() const { return m_view; }
00220 private:
00221     KoView* m_view;
00222 };
00223 
00224 KoBrowserExtension::KoBrowserExtension( KoDocument * doc )
00225     : KParts::BrowserExtension( doc)
00226 {
00227     emit enableAction( "print", true );
00228 }
00229 
00230 void KoBrowserExtension::print()
00231 {
00232     KoDocument * doc = static_cast<KoDocument *>( parent() );
00233     KoViewWrapperWidget * wrapper = static_cast<KoViewWrapperWidget *>( doc->widget() );
00234     KoView * view = wrapper->koView();
00235     // TODO remove code duplication (KoMainWindow), by moving this to KoView
00236     KPrinter printer;
00237     // ### TODO: apply global koffice settings here
00238     view->setupPrinter( printer );
00239     if ( printer.setup( view ) )
00240         view->print( printer );
00241 }
00242 
00243 KoDocument::KoDocument( QWidget * parentWidget, QObject* parent, bool singleViewMode )
00244     : KParts::ReadWritePart( parent )
00245 {
00246     if(s_documentList==0L)
00247         s_documentList=new Q3PtrList<KoDocument>;
00248     s_documentList->append(this);
00249 
00250     d = new Private;
00251     m_bEmpty = true;
00252     connect( &d->m_autoSaveTimer, SIGNAL( timeout() ), this, SLOT( slotAutoSave() ) );
00253     setAutoSave( s_defaultAutoSave );
00254     d->m_bSingleViewMode = singleViewMode;
00255 
00256     setObjectName( newObjectName() );
00257     new KoDocumentAdaptor(this);
00258     QDBusConnection::sessionBus().registerObject( '/' + objectName(), this);
00259 
00260 
00261     // the parent setting *always* overrides! (Simon)
00262     if ( parent )
00263     {
00264         if ( parent->inherits( "KoDocument" ) )
00265             d->m_bSingleViewMode = ((KoDocument *)parent)->isSingleViewMode();
00266         else if ( parent->inherits( "KParts::Part" ) )
00267             d->m_bSingleViewMode = true;
00268     }
00269 
00270     if ( singleViewMode )
00271     {
00272         d->m_wrapperWidget = new KoViewWrapperWidget( parentWidget );
00273         setWidget( d->m_wrapperWidget );
00274         kDebug(30003) << "creating KoBrowserExtension" << endl;
00275         (void) new KoBrowserExtension( this ); // ## only if embedded into a browser?
00276     }
00277 
00278     d->m_docInfo = new KoDocumentInfo( this );
00279 
00280     m_pageLayout.ptWidth = 0;
00281     m_pageLayout.ptHeight = 0;
00282     m_pageLayout.ptTop = 0;
00283     m_pageLayout.ptBottom = 0;
00284     m_pageLayout.ptLeft = 0;
00285     m_pageLayout.ptRight = 0;
00286 
00287     // A way to 'fix' the job's window, since we have no widget known to KParts
00288     if ( !singleViewMode )
00289         connect( this, SIGNAL( started( KIO::Job* ) ), SLOT( slotStarted( KIO::Job* ) ) );
00290 }
00291 
00292 KoDocument::~KoDocument()
00293 {
00294     d->m_autoSaveTimer.stop();
00295 
00296     Q3PtrListIterator<KoDocumentChild> childIt( d->m_children );
00297     for (; childIt.current(); ++childIt )
00298         disconnect( childIt.current(), SIGNAL( destroyed() ),
00299                     this, SLOT( slotChildDestroyed() ) );
00300 
00301     // Tell our views that the document is already destroyed and
00302     // that they shouldn't try to access it.
00303     foreach ( KoView* view, d->m_views )
00304         view->setDocumentDeleted();
00305 
00306     delete d->m_startUpWidget;
00307     d->m_startUpWidget = 0;
00308 
00309     d->m_children.setAutoDelete( true );
00310     d->m_children.clear();
00311 
00312     d->m_shells.setAutoDelete( true );
00313     d->m_shells.clear();
00314 
00315 //     delete d->m_dcopObject;
00316     delete d->filterManager;
00317     delete d;
00318     s_documentList->removeRef(this);
00319     // last one?
00320     if(s_documentList->isEmpty()) {
00321         delete s_documentList;
00322         s_documentList=0;
00323     }
00324 }
00325 
00326 bool KoDocument::isSingleViewMode() const
00327 {
00328     return d->m_bSingleViewMode;
00329 }
00330 
00331 bool KoDocument::isEmbedded() const
00332 {
00333     return dynamic_cast<KoDocument *>( parent() ) != 0;
00334 }
00335 
00336 KoView *KoDocument::createView( QWidget *parent )
00337 {
00338     KoView *view=createViewInstance(parent);
00339     addView(view);
00340     return view;
00341 }
00342 
00343 bool KoDocument::exp0rt( const KUrl & _url )
00344 {
00345     bool ret;
00346 
00347     d->m_isExporting = true;
00348 
00349     //
00350     // Preserve a lot of state here because we need to restore it in order to
00351     // be able to fake a File --> Export.  Can't do this in saveFile() because,
00352     // for a start, KParts has already set m_url and m_file and because we need
00353     // to restore the modified flag etc. and don't want to put a load on anyone
00354     // reimplementing saveFile() (Note: import() and export() will remain
00355     // non-virtual).
00356     //
00357     KUrl oldURL = m_url;
00358     QString oldFile = m_file;
00359 
00360     bool wasModified = isModified ();
00361     QByteArray oldMimeType = mimeType ();
00362 
00363 
00364     // save...
00365     ret = saveAs( _url );
00366 
00367 
00368     //
00369     // This is sooooo hacky :(
00370     // Hopefully we will restore enough state.
00371     //
00372     kDebug(30003) << "Restoring KoDocument state to before export" << endl;
00373 
00374     // always restore m_url & m_file because KParts has changed them
00375     // (regardless of failure or success)
00376     m_url = oldURL;
00377     m_file = oldFile;
00378 
00379     // on successful export we need to restore modified etc. too
00380     // on failed export, mimetype/modified hasn't changed anyway
00381     if (ret)
00382     {
00383         setModified (wasModified);
00384         d->mimeType = oldMimeType;
00385     }
00386 
00387 
00388     d->m_isExporting = false;
00389 
00390     return ret;
00391 }
00392 
00393 bool KoDocument::saveFile()
00394 {
00395     kDebug(30003) << "KoDocument::saveFile() doc='" << url().url() <<"'"<< endl;
00396 
00397     // Save it to be able to restore it after a failed save
00398     const bool wasModified = isModified ();
00399 
00400     // The output format is set by koMainWindow, and by openFile
00401     QByteArray outputMimeType = d->outputMimeType;
00402     //Q_ASSERT( !outputMimeType.isEmpty() ); // happens when using the DCOP method saveAs
00403     if ( outputMimeType.isEmpty() )
00404         outputMimeType = d->outputMimeType = nativeFormatMimeType();
00405 
00406     QApplication::setOverrideCursor( Qt::WaitCursor );
00407 
00408     if ( backupFile() ) {
00409         if ( url().isLocalFile() )
00410             KSaveFile::backupFile( url().path(), d->m_backupPath );
00411         else {
00412             KIO::UDSEntry entry;
00413             if ( KIO::NetAccess::stat( url(), entry, shells().current() ) ) { // this file exists => backup
00414                 emit sigStatusBarMessage( i18n("Making backup...") );
00415                 KUrl backup;
00416                 if ( d->m_backupPath.isEmpty())
00417                     backup = url();
00418                 else
00419                     backup = d->m_backupPath +'/'+url().fileName();
00420                 backup.setPath( backup.path() + QString::fromLatin1("~") );
00421                 KFileItem item( entry, url() );
00422                 Q_ASSERT( item.name() == url().fileName() );
00423                 KIO::NetAccess::file_copy( url(), backup, item.permissions(), true /*overwrite*/, false /*resume*/, shells().current() );
00424             }
00425         }
00426     }
00427 
00428     emit sigStatusBarMessage( i18n("Saving...") );
00429     bool ret = false;
00430     bool suppressErrorDialog = false;
00431     if ( !isNativeFormat( outputMimeType ) ) {
00432         kDebug(30003) << "Saving to format " << outputMimeType << " in " << m_file << endl;
00433         // Not native format : save using export filter
00434         if ( !d->filterManager )
00435             d->filterManager = new KoFilterManager( this );
00436 
00437         KoFilter::ConversionStatus status = d->filterManager->exp0rt( m_file, outputMimeType );
00438         ret = status == KoFilter::OK;
00439         suppressErrorDialog = (status == KoFilter::UserCancelled || status == KoFilter::BadConversionGraph );
00440     } else {
00441         // Native format => normal save
00442         Q_ASSERT( !m_file.isEmpty() );
00443         ret = saveNativeFormat( m_file );
00444     }
00445 
00446     if ( ret ) {
00447         removeAutoSaveFiles();
00448         // Restart the autosave timer
00449         // (we don't want to autosave again 2 seconds after a real save)
00450         setAutoSave( d->m_autoSaveDelay );
00451     }
00452 
00453     QApplication::restoreOverrideCursor();
00454     if ( !ret )
00455     {
00456         if ( !suppressErrorDialog )
00457         {
00458             showSavingErrorDialog();
00459         }
00460 
00461         // couldn't save file so this new URL is invalid
00462         // FIXME: we should restore the current document's true URL instead of
00463         // setting it to nothing otherwise anything that depends on the URL
00464         // being correct will not work (i.e. the document will be called
00465         // "Untitled" which may not be true)
00466         //
00467         // Update: now the URL is restored in KoMainWindow but really, this
00468         // should still be fixed in KoDocument/KParts (ditto for m_file).
00469         // We still resetURL() here since we may or may not have been called
00470         // by KoMainWindow - Clarence
00471         resetURL();
00472 
00473         // As we did not save, restore the "was modified" status
00474         setModified( wasModified );
00475     }
00476 
00477     if ( ret )
00478     {
00479         d->mimeType = outputMimeType;
00480         setConfirmNonNativeSave ( isExporting (), false );
00481     }
00482     emit sigClearStatusBarMessage();
00483 
00484     return ret;
00485 }
00486 
00487 QByteArray KoDocument::mimeType() const
00488 {
00489     return d->mimeType;
00490 }
00491 
00492 void KoDocument::setMimeType( const QByteArray & mimeType )
00493 {
00494     d->mimeType = mimeType;
00495 }
00496 
00497 void KoDocument::setOutputMimeType( const QByteArray & mimeType, int specialOutputFlag )
00498 {
00499     d->outputMimeType = mimeType;
00500     d->m_specialOutputFlag = specialOutputFlag;
00501 }
00502 
00503 QByteArray KoDocument::outputMimeType() const
00504 {
00505     return d->outputMimeType;
00506 }
00507 
00508 int KoDocument::specialOutputFlag() const
00509 {
00510     return d->m_specialOutputFlag;
00511 }
00512 
00513 bool KoDocument::confirmNonNativeSave( const bool exporting ) const
00514 {
00515     // "exporting ? 1 : 0" is different from "exporting" because a bool is
00516     // usually implemented like an "int", not "unsigned : 1"
00517     return d->m_confirmNonNativeSave [ exporting ? 1 : 0 ];
00518 }
00519 
00520 void KoDocument::setConfirmNonNativeSave( const bool exporting, const bool on )
00521 {
00522     d->m_confirmNonNativeSave [ exporting ? 1 : 0] = on;
00523 }
00524 
00525 bool KoDocument::wantExportConfirmation() const
00526 {
00527     return true;
00528 }
00529 
00530 bool KoDocument::isImporting() const
00531 {
00532     return d->m_isImporting;
00533 }
00534 
00535 bool KoDocument::isExporting() const
00536 {
00537     return d->m_isExporting;
00538 }
00539 
00540 void KoDocument::setCheckAutoSaveFile( bool b )
00541 {
00542     d->m_shouldCheckAutoSaveFile = b;
00543 }
00544 
00545 void KoDocument::setAutoErrorHandlingEnabled( bool b )
00546 {
00547     d->m_autoErrorHandlingEnabled = b;
00548 }
00549 
00550 bool KoDocument::isAutoErrorHandlingEnabled() const
00551 {
00552     return d->m_autoErrorHandlingEnabled;
00553 }
00554 
00555 void KoDocument::slotAutoSave()
00556 {
00557     if ( isModified() && d->modifiedAfterAutosave && !d->m_bLoading )
00558     {
00559         connect( this, SIGNAL( sigProgress( int ) ), shells().current(), SLOT( slotProgress( int ) ) );
00560         emit sigStatusBarMessage( i18n("Autosaving...") );
00561         d->m_autosaving = true;
00562         bool ret = saveNativeFormat( autoSaveFile( m_file ) );
00563         setModified( true );
00564         if ( ret ) {
00565             d->modifiedAfterAutosave = false;
00566             d->m_autoSaveTimer.stop(); // until the next change
00567         }
00568         d->m_autosaving = false;
00569         emit sigClearStatusBarMessage();
00570         disconnect( this, SIGNAL( sigProgress( int ) ), shells().current(), SLOT( slotProgress( int ) ) );
00571         if ( !ret )
00572             emit sigStatusBarMessage( i18n("Error during autosave! Partition full?") );
00573     }
00574 }
00575 
00576 KAction *KoDocument::action( const QDomElement &element ) const
00577 {
00578     // First look in the document itself
00579     KAction* act = KParts::ReadWritePart::action( element );
00580     if ( act )
00581         return act;
00582 
00583     Q_ASSERT( d->m_bSingleViewMode );
00584     // Then look in the first view (this is for the single view mode)
00585     if ( !d->m_views.isEmpty() )
00586         return d->m_views.first()->action( element );
00587     else
00588         return 0L;
00589 }
00590 
00591 QDomDocument KoDocument::domDocument() const
00592 {
00593     // When embedded into e.g. konqueror, we want the view's GUI (hopefully a reduced one)
00594     // to be used.
00595     Q_ASSERT( d->m_bSingleViewMode );
00596     if ( d->m_views.isEmpty() )
00597         return QDomDocument();
00598     else
00599         return d->m_views.first()->domDocument();
00600 }
00601 
00602 void KoDocument::setManager( KParts::PartManager *manager )
00603 {
00604     KParts::ReadWritePart::setManager( manager );
00605     if ( d->m_bSingleViewMode && d->m_views.count() == 1 )
00606         d->m_views.first()->setPartManager( manager );
00607 
00608     if ( manager )
00609     {
00610         Q3PtrListIterator<KoDocumentChild> it( d->m_children );
00611         for (; it.current(); ++it )
00612             if ( it.current()->document() )
00613                 manager->addPart( it.current()->document(), false );
00614     }
00615 }
00616 
00617 void KoDocument::setReadWrite( bool readwrite )
00618 {
00619     KParts::ReadWritePart::setReadWrite( readwrite );
00620 
00621     foreach ( KoView* view, d->m_views )
00622         view->updateReadWrite( readwrite );
00623 
00624     Q3PtrListIterator<KoDocumentChild> dIt( d->m_children );
00625     for (; dIt.current(); ++dIt )
00626         if ( dIt.current()->document() )
00627             dIt.current()->document()->setReadWrite( readwrite );
00628 
00629      Q3PtrListIterator<KoMainWindow> it( d->m_shells );
00630      for (; it.current(); ++it )
00631        it.current()->setReadWrite( readwrite );
00632 
00633     setAutoSave( d->m_autoSaveDelay );
00634 }
00635 
00636 void KoDocument::setAutoSave( int delay )
00637 {
00638     d->m_autoSaveDelay = delay;
00639     if ( isReadWrite() && !isEmbedded() && d->m_autoSaveDelay > 0 )
00640         d->m_autoSaveTimer.start( d->m_autoSaveDelay * 1000 );
00641     else
00642         d->m_autoSaveTimer.stop();
00643 }
00644 
00645 void KoDocument::addView( KoView *view )
00646 {
00647     if ( !view )
00648         return;
00649 
00650     d->m_views.append( view );
00651     view->updateReadWrite( isReadWrite() );
00652 }
00653 
00654 void KoDocument::removeView( KoView *view )
00655 {
00656     d->m_views.removeAll( view );
00657 }
00658 
00659 const QList<KoView*>& KoDocument::views() const
00660 {
00661     return d->m_views;
00662 }
00663 
00664 int KoDocument::viewCount() const
00665 {
00666     return d->m_views.count();
00667 }
00668 
00669 void KoDocument::insertChild( KoDocumentChild *child )
00670 {
00671     setModified( true );
00672 
00673     d->m_children.append( child );
00674 
00675     connect( child, SIGNAL( changed( KoChild * ) ),
00676              this, SLOT( slotChildChanged( KoChild * ) ) );
00677     connect( child, SIGNAL( destroyed() ),
00678              this, SLOT( slotChildDestroyed() ) );
00679 
00680     // It may be that insertChild is called without the KoDocumentChild
00681     // having a KoDocument attached, yet. This happens for example
00682     // when KPresenter loads a document with embedded objects. For those
00683     // KPresenterChild objects are allocated and insertChild is called.
00684     // Later in loadChildren() KPresenter iterates over the child list
00685     // and calls loadDocument for each child. That's exactly where we
00686     // will try to do what we cannot do now: Register the child document
00687     // at the partmanager (Simon)
00688     if ( manager() && !isSingleViewMode() && child->document() )
00689         manager()->addPart( child->document(), false );
00690 }
00691 
00692 void KoDocument::slotChildChanged( KoChild *c )
00693 {
00694     assert( c->inherits( "KoDocumentChild" ) );
00695     emit childChanged( static_cast<KoDocumentChild *>( c ) );
00696 }
00697 
00698 void KoDocument::slotChildDestroyed()
00699 {
00700     setModified( true );
00701 
00702     const KoDocumentChild *child = static_cast<const KoDocumentChild *>( sender() );
00703     d->m_children.removeRef( child );
00704 }
00705 
00706 const Q3PtrList<KoDocumentChild>& KoDocument::children() const
00707 {
00708     return d->m_children;
00709 }
00710 
00711 KParts::Part *KoDocument::hitTest( QWidget *widget, const QPoint &globalPos )
00712 {
00713     foreach ( KoView* view, d->m_views )
00714         if ( static_cast<QWidget *>(view) == widget )
00715         {
00716             QPoint canvasPos( view->canvas()->mapFromGlobal( globalPos ) );
00717             canvasPos.rx() += view->canvasXOffset();
00718             canvasPos.ry() += view->canvasYOffset();
00719 
00720             KParts::Part *part = view->hitTest( canvasPos );
00721             if ( part )
00722                 return part;
00723         }
00724 
00725     return 0L;
00726 }
00727 
00728 KoDocument* KoDocument::hitTest( const QPoint &pos, KoView* view, const QMatrix &matrix )
00729 {
00730     // Call KoDocumentChild::hitTest for any child document
00731     Q3PtrListIterator<KoDocumentChild> it( d->m_children );
00732     for (; it.current(); ++it )
00733     {
00734         KoDocument *doc = it.current()->hitTest( pos, view, matrix );
00735         if ( doc )
00736             return doc;
00737     }
00738 
00739     // Unless we hit an embedded document, the hit is on this document itself.
00740     return this;
00741 }
00742 
00743 KoDocumentChild *KoDocument::child( KoDocument *doc )
00744 {
00745     Q3PtrListIterator<KoDocumentChild> it( d->m_children );
00746     for (; it.current(); ++it )
00747         if ( it.current()->document() == doc )
00748             return it.current();
00749 
00750     return 0L;
00751 }
00752 
00753 KoDocumentInfo *KoDocument::documentInfo() const
00754 {
00755     return d->m_docInfo;
00756 }
00757 
00758 void KoDocument::setViewBuildDocument( KoView *view, const QDomDocument &doc )
00759 {
00760     int viewIdx = d->m_views.indexOf( view );
00761     if ( viewIdx == -1 )
00762         return;
00763 
00764     if ( d->m_viewBuildDocuments.count() == viewIdx )
00765         d->m_viewBuildDocuments.append( doc );
00766     else if ( d->m_viewBuildDocuments.count() > viewIdx )
00767         d->m_viewBuildDocuments[ viewIdx ] = doc;
00768 }
00769 
00770 QDomDocument KoDocument::viewBuildDocument( KoView *view )
00771 {
00772     QDomDocument res;
00773 
00774     int viewIdx = d->m_views.indexOf( view );
00775     if ( viewIdx == -1 )
00776         return res;
00777 
00778     if ( viewIdx >= d->m_viewBuildDocuments.count() )
00779         return res;
00780 
00781     res = d->m_viewBuildDocuments[ viewIdx ];
00782 
00783     // make this entry empty. otherwise we get a segfault in QMap ;-(
00784     d->m_viewBuildDocuments[ viewIdx ] = QDomDocument();
00785 
00786     return res;
00787 }
00788 
00789 void KoDocument::paintEverything( QPainter &painter, const QRect &rect, bool transparent, KoView *view, double zoomX, double zoomY )
00790 {
00791     paintContent( painter, rect, transparent, zoomX, zoomY );
00792     paintChildren( painter, rect, view, zoomX, zoomY );
00793 }
00794 
00795 void KoDocument::paintChildren( QPainter &painter, const QRect &/*rect*/, KoView *view, double zoomX, double zoomY )
00796 {
00797     Q3PtrListIterator<KoDocumentChild> it( d->m_children );
00798     for (; it.current(); ++it )
00799     {
00800         // #### todo: paint only if child is visible inside rect
00801         painter.save();
00802         paintChild( it.current(), painter, view, zoomX, zoomY );
00803         painter.restore();
00804     }
00805 }
00806 
00807 void KoDocument::paintChild( KoDocumentChild *child, QPainter &painter, KoView *view, double zoomX, double zoomY )
00808 {
00809     if ( child->isDeleted() )
00810         return;
00811 
00812     // QRegion rgn = painter.clipRegion();
00813 
00814     child->transform( painter );
00815     child->document()->paintEverything( painter, child->contentRect(), child->isTransparent(), view, zoomX, zoomY );
00816 
00817     if ( view && view->partManager() )
00818     {
00819         // ### do we need to apply zoomX and zoomY here ?
00820         KParts::PartManager *manager = view->partManager();
00821 
00822         painter.scale( 1.0 / child->xScaling(), 1.0 / child->yScaling() );
00823 
00824         int w = int( (double)child->contentRect().width() * child->xScaling() );
00825         int h = int( (double)child->contentRect().height() * child->yScaling() );
00826         if ( ( manager->selectedPart() == (KParts::Part *)child->document() &&
00827                manager->selectedWidget() == (QWidget *)view ) ||
00828              ( manager->activePart() == (KParts::Part *)child->document() &&
00829                manager->activeWidget() == (QWidget *)view ) )
00830         {
00831             // painter.setClipRegion( rgn );
00832             painter.setClipping( false );
00833 
00834             painter.setPen( Qt::black );
00835             painter.fillRect( -5, -5, w + 10, 5, Qt::white );
00836             painter.fillRect( -5, h, w + 10, 5, Qt::white );
00837             painter.fillRect( -5, -5, 5, h + 10, Qt::white );
00838             painter.fillRect( w, -5, 5, h + 10, Qt::white );
00839             painter.fillRect( -5, -5, w + 10, 5, Qt::BDiagPattern );
00840             painter.fillRect( -5, h, w + 10, 5, Qt::BDiagPattern );
00841             painter.fillRect( -5, -5, 5, h + 10, Qt::BDiagPattern );
00842             painter.fillRect( w, -5, 5, h + 10, Qt::BDiagPattern );
00843 
00844             if ( manager->selectedPart() == (KParts::Part *)child->document() &&
00845                  manager->selectedWidget() == (QWidget *)view )
00846             {
00847                 QColor color;
00848                 if ( view->koDocument() == this )
00849                     color = Qt::black;
00850                 else
00851                     color = Qt::gray;
00852                 painter.fillRect( -5, -5, 5, 5, color );
00853                 painter.fillRect( -5, h, 5, 5, color );
00854                 painter.fillRect( w, h, 5, 5, color );
00855                 painter.fillRect( w, -5, 5, 5, color );
00856                 painter.fillRect( w / 2 - 3, -5, 5, 5, color );
00857                 painter.fillRect( w / 2 - 3, h, 5, 5, color );
00858                 painter.fillRect( -5, h / 2 - 3, 5, 5, color );
00859                 painter.fillRect( w, h / 2 - 3, 5, 5, color );
00860             }
00861 
00862             painter.setClipping( true );
00863         }
00864     }
00865 }
00866 
00867 bool KoDocument::isModified() const
00868 {
00869     if ( KParts::ReadWritePart::isModified() )
00870     {
00871         //kDebug(30003)<<k_funcinfo<<" Modified doc='"<<url().url()<<"' extern="<<isStoredExtern()<<endl;
00872         return true;
00873     }
00874     // Then go through internally stored children (considered to be part of this doc)
00875     Q3PtrListIterator<KoDocumentChild> it = children();
00876     for (; it.current(); ++it )
00877     {
00878         KoDocument *doc = it.current()->document();
00879         if ( doc && !it.current()->isStoredExtern() && !it.current()->isDeleted() && doc->isModified() )
00880             return true;
00881     }
00882     return false;
00883 }
00884 
00885 bool KoDocument::saveChildren( KoStore* _store )
00886 {
00887     //kDebug(30003)<<k_funcinfo<<" checking children of doc='"<<url().url()<<"'"<<endl;
00888     int i = 0;
00889     Q3PtrListIterator<KoDocumentChild> it( children() );
00890     for( ; it.current(); ++it ) {
00891         KoDocument* childDoc = it.current()->document();
00892         if (childDoc && !it.current()->isDeleted())
00893         {
00894             if ( !childDoc->isStoredExtern() )
00895             {
00896                 //kDebug(30003) << "KoDocument::saveChildren internal url: /" << i << endl;
00897                 if ( !childDoc->saveToStore( _store, QString::number( i++ ) ) )
00898                     return false;
00899 
00900                 if (!isExporting ())
00901                     childDoc->setModified( false );
00902             }
00903             //else kDebug(30003)<<k_funcinfo<<" external (don't save) url:" << childDoc->url().url()<<endl;
00904         }
00905     }
00906     return true;
00907 }
00908 
00909 bool KoDocument::saveChildrenOasis( KoStore* store, KoXmlWriter* manifestWriter )
00910 {
00911     //kDebug(30003)<<k_funcinfo<<" checking children of doc='"<<url().url()<<"'"<<endl;
00912     Q3PtrListIterator<KoDocumentChild> it( children() );
00913     for( ; it.current(); ++it ) {
00914         KoDocument* childDoc = it.current()->document();
00915         if ( childDoc && !it.current()->isDeleted() )
00916         {
00917             if ( !it.current()->saveOasis( store, manifestWriter ) )
00918                 return false;
00919             if ( !childDoc->isStoredExtern() && !isExporting () )
00920                 childDoc->setModified( false );
00921         }
00922     }
00923     return true;
00924 }
00925 
00926 bool KoDocument::saveExternalChildren()
00927 {
00928     if ( d->m_doNotSaveExtDoc )
00929     {
00930         //kDebug(30003)<<k_funcinfo<<" Don't save external docs in doc='"<<url().url()<<"'"<<endl;
00931         d->m_doNotSaveExtDoc = false;
00932         return true;
00933     }
00934 
00935     //kDebug(30003)<<k_funcinfo<<" checking children of doc='"<<url().url()<<"'"<<endl;
00936     KoDocumentChild *ch;
00937     Q3PtrListIterator<KoDocumentChild> it = children();
00938     for (; (ch = it.current()); ++it )
00939     {
00940         if ( !ch->isDeleted() )
00941         {
00942             KoDocument* doc = ch->document();
00943             if ( doc && doc->isStoredExtern() && doc->isModified() )
00944             {
00945                 kDebug(30003)<<" save external doc='"<<url().url()<<"'"<<endl;
00946                 doc->setDoNotSaveExtDoc(); // Only save doc + it's internal children
00947                 if ( !doc->save() )
00948                     return false; // error
00949             }
00950             //kDebug(30003)<<k_funcinfo<<" not modified doc='"<<url().url()<<"'"<<endl;
00951             // save possible external docs inside doc
00952             if ( doc && !doc->saveExternalChildren() )
00953                 return false;
00954         }
00955     }
00956     return true;
00957 }
00958 
00959 bool KoDocument::saveNativeFormat( const QString & file )
00960 {
00961     d->lastErrorMessage.clear();
00962     //kDebug(30003) << "Saving to store" << endl;
00963 
00964     KoStore::Backend backend = KoStore::Auto;
00965 #if 0
00966     if ( d->m_specialOutputFlag == SaveAsKOffice1dot1 )
00967     {
00968         kDebug(30003) << "Saving as KOffice-1.1 format, using a tar.gz" << endl;
00969         backend = KoStore::Tar; // KOffice-1.0/1.1 used tar.gz for the native mimetype
00971     }
00972     else
00973 #endif
00974         if ( d->m_specialOutputFlag == SaveAsDirectoryStore )
00975     {
00976         backend = KoStore::Directory;
00977         kDebug(30003) << "Saving as uncompressed XML, using directory store." << endl;
00978     }
00979     else if ( d->m_specialOutputFlag == SaveAsFlatXML )
00980     {
00981         kDebug(30003) << "Saving as a flat XML file." << endl;
00982         QFile f( file );
00983         if ( f.open( QIODevice::WriteOnly | QIODevice::Text ) )
00984         {
00985             bool success = saveToStream( &f );
00986             f.close();
00987             return success;
00988         }
00989         else
00990             return false;
00991     }
00992 
00993     kDebug(30003) << "KoDocument::saveNativeFormat nativeFormatMimeType=" << nativeFormatMimeType() << endl;
00994     // OLD: bool oasis = d->m_specialOutputFlag == SaveAsOASIS;
00995     // OLD: QCString mimeType = oasis ? nativeOasisMimeType() : nativeFormatMimeType();
00996     QByteArray mimeType = d->outputMimeType;
00997     QByteArray nativeOasisMime = nativeOasisMimeType();
00998     bool oasis = !mimeType.isEmpty() && ( mimeType == nativeOasisMime || mimeType == nativeOasisMime + "-template" );
00999     // TODO: use std::auto_ptr or create store on stack [needs API fixing],
01000     // to remove all the 'delete store' in all the branches
01001     KoStore* store = KoStore::createStore( file, KoStore::Write, mimeType, backend );
01002     if ( store->bad() )
01003     {
01004         d->lastErrorMessage = i18n( "Could not create the file for saving" ); // more details needed?
01005         delete store;
01006         return false;
01007     }
01008 
01009     if ( oasis )
01010     {
01011         kDebug(30003) << "Saving to OASIS format" << endl;
01012         // Tell KoStore not to touch the file names
01013         store->disallowNameExpansion();
01014         KoOasisStore oasisStore( store );
01015         KoXmlWriter* manifestWriter = oasisStore.manifestWriter( mimeType );
01016 
01017         if ( !saveOasis( store, manifestWriter ) )
01018         {
01019             kDebug(30003) << "saveOasis failed" << endl;
01020             delete store;
01021             return false;
01022         }
01023 
01024         // Save embedded objects
01025         if ( !saveChildrenOasis( store, manifestWriter ) )
01026         {
01027             kDebug(30003) << "saveChildrenOasis failed" << endl;
01028             delete store;
01029             return false;
01030         }
01031 
01032         if ( store->open( "meta.xml" ) )
01033         {
01034             if ( !d->m_docInfo->saveOasis( store ) || !store->close() ) {
01035                 delete store;
01036                 return false;
01037             }
01038             manifestWriter->addManifestEntry( "meta.xml", "text/xml" );
01039         }
01040         else
01041         {
01042             d->lastErrorMessage = i18n( "Not able to write '%1'. Partition full?", QString("meta.xml") );
01043             delete store;
01044             return false;
01045         }
01046 
01047         if ( store->open( "Thumbnails/thumbnail.png" ) )
01048         {
01049             if ( !saveOasisPreview( store, manifestWriter ) || !store->close() ) {
01050                 d->lastErrorMessage = i18n( "Error while trying to write '%1'. Partition full?", QString("Thumbnails/thumbnail.png") );
01051                 delete store;
01052                 return false;
01053             }
01054             // No manifest entry!
01055         }
01056         else
01057         {
01058             d->lastErrorMessage = i18n( "Not able to write '%1'. Partition full?", QString("Thumbnails/thumbnail.png") );
01059             delete store;
01060             return false;
01061         }
01062 
01063         if ( !d->m_versionInfo.isEmpty() )
01064         {
01065           if ( store->open( "VersionList.xml" ) )
01066           {
01067             KoStoreDevice dev( store );
01068             KoXmlWriter* xmlWriter = KoDocument::createOasisXmlWriter( &dev,
01069                 "VL:version-list" );
01070             for (int i = 0; i < d->m_versionInfo.size(); ++i)
01071             {
01072                 KoVersionInfo *version = &d->m_versionInfo[i];
01073                 xmlWriter->startElement( "VL:version-entry" );
01074                 xmlWriter->addAttribute( "VL:title", version->title );
01075                 xmlWriter->addAttribute( "VL:comment", version->comment );
01076                 xmlWriter->addAttribute( "VL:creator", version->saved_by );
01077                 xmlWriter->addAttribute( "dc:date-time", version->date.toString(Qt::ISODate) );
01078                 xmlWriter->endElement();
01079             }
01080             xmlWriter->endElement(); // root element
01081             xmlWriter->endDocument();
01082             delete xmlWriter;
01083             store->close();
01084             manifestWriter->addManifestEntry( "VersionList.xml", "text/xml" );
01085 
01086             for (int i = 0; i < d->m_versionInfo.size(); ++i)
01087             {
01088                 KoVersionInfo *version = &d->m_versionInfo[i];
01089                 store->addDataToFile( version->data, "Versions/" + version->title );
01090             }
01091           }
01092           else
01093           {
01094             d->lastErrorMessage = i18n( "Not able to write '%1'. Partition full?", QString("VersionList.xml") );
01095             delete store;
01096             return false;
01097           }
01098          }
01099 
01100         // Write out manifest file
01101         if ( !oasisStore.closeManifestWriter() )
01102         {
01103             d->lastErrorMessage = i18n( "Error while trying to write '%1'. Partition full?", QString("META-INF/manifest.xml") );
01104             delete store;
01105             return false;
01106         }
01107 
01108         delete store;
01109     }
01110     else
01111     {
01112         // Save internal children first since they might get a new url
01113         if ( !saveChildren( store ) && !oasis )
01114         {
01115             if ( d->lastErrorMessage.isEmpty() )
01116                 d->lastErrorMessage = i18n( "Error while saving embedded documents" ); // more details needed
01117             delete store;
01118             return false;
01119         }
01120 
01121         kDebug(30003) << "Saving root" << endl;
01122         if ( store->open( "root" ) )
01123         {
01124             KoStoreDevice dev( store );
01125             if ( !saveToStream( &dev ) || !store->close() )
01126             {
01127                 kDebug(30003) << "saveToStream failed" << endl;
01128                 delete store;
01129                 return false;
01130             }
01131         }
01132         else
01133         {
01134             d->lastErrorMessage = i18n( "Not able to write '%1'. Partition full?", QString( "maindoc.xml") );
01135             delete store;
01136             return false;
01137         }
01138         if ( store->open( "documentinfo.xml" ) )
01139         {
01140             QDomDocument doc = d->m_docInfo->save();
01141             KoStoreDevice dev( store );
01142 
01143             QByteArray s = doc.toByteArray(); // this is already Utf8!
01144             (void)dev.write( s.data(), s.size() );
01145             (void)store->close();
01146         }
01147 
01148         if ( store->open( "preview.png" ) )
01149         {
01150             // ### TODO: missing error checking (The partition could be full!)
01151             savePreview( store );
01152             (void)store->close();
01153         }
01154 
01155         if ( !completeSaving( store ) )
01156         {
01157             delete store;
01158             return false;
01159         }
01160         kDebug(30003) << "Saving done of url: " << url().url() << endl;
01161         delete store;
01162     }
01163     if ( !saveExternalChildren() )
01164     {
01165         return false;
01166     }
01167     return true;
01168 }
01169 
01170 bool KoDocument::saveToStream( QIODevice * dev )
01171 {
01172     QDomDocument doc = saveXML();
01173     // Save to buffer
01174     QByteArray s = doc.toByteArray(); // utf8 already
01175     dev->open( QIODevice::WriteOnly );
01176     int nwritten = dev->write( s.data(), s.size() );
01177     if ( nwritten != (int)s.size() )
01178       kWarning(30003) << "KoDocument::saveToStream wrote " << nwritten << "   - expected " <<  s.size() << endl;
01179     return nwritten == (int)s.size();
01180 }
01181 
01182 // Called for embedded documents
01183 bool KoDocument::saveToStore( KoStore* _store, const QString & _path )
01184 {
01185     kDebug(30003) << "Saving document to store " << _path << endl;
01186 
01187     // Use the path as the internal url
01188     if ( _path.startsWith( STORE_PROTOCOL ) )
01189         m_url = KUrl( _path );
01190     else // ugly hack to pass a relative URI
01191         m_url = KUrl( INTERNAL_PREFIX +  _path );
01192 
01193     // To make the children happy cd to the correct directory
01194     _store->pushDirectory();
01195     _store->enterDirectory( _path );
01196 
01197     // Save childen first since they might get a new url
01198     if ( !saveChildren( _store ) )
01199         return false;
01200 
01201     // In the current directory we're the king :-)
01202     if ( _store->open( "root" ) )
01203     {
01204         KoStoreDevice dev( _store );
01205         if ( !saveToStream( &dev ) )
01206         {
01207             _store->close();
01208             return false;
01209         }
01210         if ( !_store->close() )
01211             return false;
01212     }
01213 
01214     if ( !completeSaving( _store ) )
01215         return false;
01216 
01217     // Now that we're done leave the directory again
01218     _store->popDirectory();
01219 
01220     kDebug(30003) << "Saved document to store" << endl;
01221 
01222     return true;
01223 }
01224 
01225 bool KoDocument::saveOasisPreview( KoStore* store, KoXmlWriter* manifestWriter )
01226 {
01227     const QPixmap pix = generatePreview( QSize( 128, 128 ) );
01228     QImage preview ( pix.toImage().convertToFormat( QImage::Format_ARGB32, Qt::ColorOnly ) );
01229 
01230     // ### TODO: freedesktop.org Thumbnail specification (date...)
01231     KoStoreDevice io ( store );
01232     if ( !io.open( QIODevice::WriteOnly ) )
01233         return false;
01234     if ( ! preview.save( &io, "PNG", 0 ) )
01235         return false;
01236     io.close();
01237     manifestWriter->addManifestEntry( "Thumbnails/", "" );
01238     manifestWriter->addManifestEntry( "Thumbnails/thumbnail.png", "" );
01239     return true;
01240 }
01241 
01242 bool KoDocument::savePreview( KoStore* store )
01243 {
01244     QPixmap pix = generatePreview(QSize(256, 256));
01245     // Reducing to 8bpp reduces file sizes quite a lot.
01246     const QImage preview( pix.toImage().convertToFormat( QImage::Format_Indexed8, Qt::AvoidDither | Qt::DiffuseDither ) );
01247     KoStoreDevice io ( store );
01248     if ( !io.open( QIODevice::WriteOnly ) )
01249         return false;
01250     if ( ! preview.save( &io, "PNG" ) ) // ### TODO What is -9 in quality terms?
01251         return false;
01252     io.close();
01253     return true;
01254 }
01255 
01256 QPixmap KoDocument::generatePreview( const QSize& size )
01257 {
01258     double docWidth, docHeight;
01259     int pixmapSize = qMax(size.width(), size.height());
01260 
01261     if (m_pageLayout.ptWidth > 1.0) {
01262         docWidth = m_pageLayout.ptWidth / 72 * KoGlobal::dpiX();
01263         docHeight = m_pageLayout.ptHeight / 72 * KoGlobal::dpiY();
01264 
01265     } else {
01266         // If we don't have a page layout, just draw the top left hand corner
01267         docWidth = 500.0;
01268         docHeight = 500.0;
01269     }
01270 
01271     double ratio = docWidth / docHeight;
01272 
01273     int previewWidth, previewHeight;
01274     if (ratio > 1.0)
01275     {
01276         previewWidth = (int) pixmapSize;
01277         previewHeight = (int) (pixmapSize / ratio);
01278     }
01279     else
01280     {
01281         previewWidth = (int) (pixmapSize * ratio);
01282         previewHeight = (int) pixmapSize;
01283     }
01284 
01285     QPixmap pix( (int)docWidth, (int)docHeight );
01286 
01287     pix.fill( QColor( 245, 245, 245 ) );
01288 
01289     QRect rc(0, 0, pix.width(), pix.height());
01290 
01291     QPainter p;
01292     p.begin(&pix);
01293     paintEverything(p, rc, false);
01294     p.end();
01295 
01296     return pix.scaled(QSize(previewWidth, previewHeight), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
01297 }
01298 
01299 QString KoDocument::autoSaveFile( const QString & path ) const
01300 {
01301     // Using the extension allows to avoid relying on the mime magic when opening
01302     KMimeType::Ptr mime = KMimeType::mimeType( nativeFormatMimeType() );
01303     Q_ASSERT(mime);
01304     QString extension = mime->property( "X-KDE-NativeExtension" ).toString();
01305     if ( path.isEmpty() )
01306     {
01307         // Never saved? Use a temp file in $HOME then
01308         // Yes, two open unnamed docs will overwrite each other's autosave file,
01309         // but hmm, we can only do something if that's in the same process anyway...
01310         QString ret = QDir::homePath() + "/." + QString::fromLatin1(instance()->instanceName()) + ".autosave" + extension;
01311         return ret;
01312     }
01313     else
01314     {
01315         KUrl url = KUrl::fromPath( path );
01316         Q_ASSERT( url.isLocalFile() );
01317         QString dir = url.directory(KUrl::AppendTrailingSlash);
01318         QString filename = url.fileName();
01319         return dir + '.' + filename + ".autosave" + extension;
01320     }
01321 }
01322 
01323 bool KoDocument::checkAutoSaveFile()
01324 {
01325     QString asf = autoSaveFile( QString::null ); // the one in $HOME
01326     //kDebug(30003) << "asf=" << asf << endl;
01327     if ( QFile::exists( asf ) )
01328     {
01329         QDateTime date = QFileInfo(asf).lastModified();
01330         QString dateStr = date.toString(Qt::LocalDate);
01331         int res = KMessageBox::warningYesNoCancel(
01332             0, i18n( "An autosaved file for an unnamed document exists in %1.\nThis file is dated %2\nDo you want to open it?",
01333             asf, dateStr ) );
01334         switch(res) {
01335         case KMessageBox::Yes : {
01336             KUrl url;
01337             url.setPath( asf );
01338             bool ret = openURL( url );
01339             if ( ret )
01340                 resetURL();
01341             return ret;
01342         }
01343         case KMessageBox::No :
01344             QFile::remove( asf );
01345             return false;
01346         default: // Cancel
01347             return false;
01348         }
01349     }
01350     return false;
01351 }
01352 
01353 bool KoDocument::import( const KUrl & _url )
01354 {
01355     bool ret;
01356 
01357     kDebug (30003) << "KoDocument::import url=" << _url.url() << endl;
01358     d->m_isImporting = true;
01359 
01360     // open...
01361     ret = openURL (_url);
01362 
01363     // reset m_url & m_file (kindly? set by KParts::openURL()) to simulate a
01364     // File --> Import
01365     if (ret)
01366     {
01367         kDebug (30003) << "KoDocument::import success, resetting url" << endl;
01368         resetURL ();
01369         setTitleModified ();
01370     }
01371 
01372     d->m_isImporting = false;
01373 
01374     return ret;
01375 }
01376 
01377 bool KoDocument::openURL( const KUrl & _url )
01378 {
01379     kDebug(30003) << "KoDocument::openURL url=" << _url.url() << endl;
01380     d->lastErrorMessage.clear();
01381 
01382     // Reimplemented, to add a check for autosave files and to improve error reporting
01383     if ( !_url.isValid() )
01384     {
01385         d->lastErrorMessage = i18n( "Malformed URL\n%1",_url.url() ); // ## used anywhere ?
01386         return false;
01387     }
01388     if ( !closeUrl() )
01389         return false;
01390 
01391     KUrl url( _url );
01392     bool autosaveOpened = false;
01393     d->m_bLoading = true;
01394     if ( url.isLocalFile() && d->m_shouldCheckAutoSaveFile )
01395     {
01396         QString file = url.path();
01397         QString asf = autoSaveFile( file );
01398         if ( QFile::exists( asf ) )
01399         {
01400             //kDebug(30003) << "KoDocument::openURL asf=" << asf << endl;
01401             // ## TODO compare timestamps ?
01402             int res = KMessageBox::warningYesNoCancel( 0,
01403                                                        i18n( "An autosaved file exists for this document.\nDo you want to open it instead?" ));
01404             switch(res) {
01405                 case KMessageBox::Yes :
01406                     url.setPath( asf );
01407                     autosaveOpened = true;
01408                     break;
01409                 case KMessageBox::No :
01410                     QFile::remove( asf );
01411                     break;
01412                 default: // Cancel
01413                     d->m_bLoading = false;
01414                     return false;
01415             }
01416         }
01417     }
01418 
01419     bool ret = KParts::ReadWritePart::openUrl( url );
01420 
01421     if ( autosaveOpened )
01422         resetURL(); // Force save to act like 'Save As'
01423     else
01424     {
01425         // We have no koffice shell when we are being embedded as a readonly part.
01426         //if ( d->m_shells.isEmpty() )
01427         //    kWarning(30003) << "KoDocument::openURL no shell yet !" << endl;
01428         // Add to recent actions list in our shells
01429         Q3PtrListIterator<KoMainWindow> it( d->m_shells );
01430         for (; it.current(); ++it )
01431             it.current()->addRecentURL( _url );
01432     }
01433     return ret;
01434 }
01435 
01436 bool KoDocument::openFile()
01437 {
01438     //kDebug(30003) << "KoDocument::openFile for " << m_file << endl;
01439     if ( !QFile::exists(m_file) )
01440     {
01441         QApplication::restoreOverrideCursor();
01442         if ( d->m_autoErrorHandlingEnabled )
01443             // Maybe offer to create a new document with that name ?
01444             KMessageBox::error(0L, i18n("The file %1 does not exist.", m_file) );
01445         d->m_bLoading = false;
01446         return false;
01447     }
01448 
01449     QApplication::setOverrideCursor( Qt::WaitCursor );
01450 
01451     d->m_specialOutputFlag = 0;
01452     QByteArray _native_format = nativeFormatMimeType();
01453 
01454     KUrl u;
01455     u.setPath( m_file );
01456     QString typeName = KMimeType::findByUrl( u, 0, true )->name();
01457 
01458     // Allow to open backup files, don't keep the mimetype application/x-trash.
01459     if ( typeName == "application/x-trash" )
01460     {
01461         QString path = u.path();
01462         KMimeType::Ptr mime = KMimeType::mimeType( typeName );
01463         QStringList patterns = mime ? mime->patterns() : QStringList();
01464         // Find the extension that makes it a backup file, and remove it
01465         for( QStringList::Iterator it = patterns.begin(); it != patterns.end(); ++it ) {
01466             QString ext = *it;
01467             if ( !ext.isEmpty() && ext[0] == '*' )
01468             {
01469                 ext.remove(0, 1);
01470                 if ( path.endsWith( ext ) ) {
01471                     path.truncate( path.length() - ext.length() );
01472                     break;
01473                 }
01474             }
01475         }
01476         typeName = KMimeType::findByPath( path, 0, true )->name();
01477     }
01478 
01479     // Special case for flat XML files (e.g. using directory store)
01480     if ( u.fileName() == "maindoc.xml" || typeName == "inode/directory" )
01481     {
01482         typeName = _native_format; // Hmm, what if it's from another app? ### Check mimetype
01483         d->m_specialOutputFlag = SaveAsDirectoryStore;
01484         kDebug(30003) << "KoDocument::openFile loading maindoc.xml, using directory store for " << m_file << endl;
01485     }
01486     kDebug(30003) << "KoDocument::openFile " << m_file << " type:" << typeName << endl;
01487 
01488     QString importedFile = m_file;
01489 
01490     if ( !isNativeFormat( typeName.toLatin1() ) ) {
01491         if ( !d->filterManager )
01492             d->filterManager = new KoFilterManager( this );
01493         KoFilter::ConversionStatus status;
01494         importedFile = d->filterManager->import( m_file, status );
01495         if ( status != KoFilter::OK )
01496         {
01497             QApplication::restoreOverrideCursor();
01498 
01499             QString msg;
01500             switch( status )
01501             {
01502                 case KoFilter::OK: break;
01503 
01504                 case KoFilter::CreationError:
01505                     msg = i18n( "Creation error" ); break;
01506 
01507                 case KoFilter::FileNotFound:
01508                     msg = i18n( "File not found" ); break;
01509 
01510                 case KoFilter::StorageCreationError:
01511                     msg = i18n( "Cannot create storage" ); break;
01512 
01513                 case KoFilter::BadMimeType:
01514                     msg = i18n( "Bad MIME type" ); break;
01515 
01516                 case KoFilter::EmbeddedDocError:
01517                     msg = i18n( "Error in embedded document" ); break;
01518 
01519                 case KoFilter::WrongFormat:
01520                     msg = i18n( "Format not recognized" ); break;
01521 
01522                 case KoFilter::NotImplemented:
01523                     msg = i18n( "Not implemented" ); break;
01524 
01525                 case KoFilter::ParsingError:
01526                     msg = i18n( "Parsing error" ); break;
01527 
01528                 case KoFilter::PasswordProtected:
01529                     msg = i18n( "Document is password protected" ); break;
01530 
01531                 case KoFilter::InternalError:
01532                 case KoFilter::UnexpectedEOF:
01533                 case KoFilter::UnexpectedOpcode:
01534                 case KoFilter::StupidError: // ?? what is this ??
01535                 case KoFilter::UsageError:
01536                     msg = i18n( "Internal error" ); break;
01537 
01538                 case KoFilter::OutOfMemory:
01539                     msg = i18n( "Out of memory" ); break;
01540 
01541                 case KoFilter::UserCancelled:
01542                 case KoFilter::BadConversionGraph:
01543                     // intentionally we do not prompt the error message here
01544                     break;
01545 
01546                 default: msg = i18n( "Unknown error" ); break;
01547             }
01548 
01549             if( d->m_autoErrorHandlingEnabled && !msg.isEmpty())
01550             {
01551                 QString docUrl = url().pathOrUrl();
01552                 QString errorMsg( i18n( "Could not open\n%2.\nReason: %1", msg, docUrl ) );
01553                 KMessageBox::error( 0, errorMsg );
01554             }
01555 
01556             d->m_bLoading = false;
01557             return false;
01558         }
01559         kDebug(30003) << "KoDocument::openFile - importedFile '" << importedFile
01560                        << "', status: " << static_cast<int>( status ) << endl;
01561     }
01562 
01563     QApplication::restoreOverrideCursor();
01564 
01565     bool ok = true;
01566 
01567     if (!importedFile.isEmpty()) // Something to load (tmp or native file) ?
01568     {
01569         // The filter, if any, has been applied. It's all native format now.
01570         if ( !loadNativeFormat( importedFile ) )
01571         {
01572             ok = false;
01573             if ( d->m_autoErrorHandlingEnabled )
01574             {
01575                 showLoadingErrorDialog();
01576             }
01577         }
01578     }
01579 
01580     if ( importedFile != m_file )
01581     {
01582         // We opened a temporary file (result of an import filter)
01583         // Set document URL to empty - we don't want to save in /tmp !
01584         // But only if in readwrite mode (no saving problem otherwise)
01585         // --
01586         // But this isn't true at all.  If this is the result of an
01587         // import, then importedFile=temporary_file.kwd and
01588         // m_file/m_url=foreignformat.ext so m_url is correct!
01589         // So don't resetURL() or else the caption won't be set when
01590         // foreign files are opened (an annoying bug).
01591         // - Clarence
01592         //
01593 #if 0
01594         if ( isReadWrite() )
01595             resetURL();
01596 #endif
01597 
01598         // remove temp file - uncomment this to debug import filters
01599         if(!importedFile.isEmpty()) {
01600             QFile::remove( importedFile );
01601         }
01602     }
01603 
01604     if ( ok && d->m_bSingleViewMode )
01605     {
01606         // See addClient below
01607         KXMLGUIFactory* guiFactory = factory();
01608         if( guiFactory ) // 0L when splitting views in konq, for some reason
01609             guiFactory->removeClient( this );
01610 
01611         if ( !d->m_views.isEmpty() )
01612         {
01613             // We already had a view (this happens when doing reload in konqueror)
01614             KoView* v = d->m_views.first();
01615             if( guiFactory )
01616                 guiFactory->removeClient( v );
01617             removeView( v );
01618             delete v;
01619             Q_ASSERT( d->m_views.isEmpty() );
01620         }
01621 
01622         KoView *view = createView( d->m_wrapperWidget );
01623         d->m_wrapperWidget->setKoView( view );
01624         view->show();
01625 
01626         // Ok, now we have a view, so action() and domDocument() will work as expected
01627         // -> rebuild GUI
01628         if ( guiFactory )
01629             guiFactory->addClient( this );
01630     }
01631 
01632     if ( ok )
01633     {
01634         setMimeTypeAfterLoading( typeName );
01635     }
01636     d->m_bLoading = false;
01637     return ok;
01638 }
01639 
01640 // shared between openFile and koMainWindow's "create new empty document" code
01641 void KoDocument::setMimeTypeAfterLoading( const QString& mimeType )
01642 {
01643     d->mimeType = mimeType.toLatin1();
01644 
01645     d->outputMimeType = d->mimeType;
01646 
01647     const bool needConfirm = !isNativeFormat( d->mimeType );
01648     setConfirmNonNativeSave( false, needConfirm  );
01649     setConfirmNonNativeSave( true, needConfirm );
01650 }
01651 
01652 // The caller must call store->close() if loadAndParse returns true.
01653 bool KoDocument::oldLoadAndParse(KoStore* store, const QString& filename, KoXmlDocument& doc)
01654 {
01655     //kDebug(30003) << "oldLoadAndParse: Trying to open " << filename << endl;
01656 
01657     if (!store->open(filename))
01658     {
01659         kWarning(30003) << "Entry " << filename << " not found!" << endl;
01660         d->lastErrorMessage = i18n( "Could not find %1",filename );
01661         return false;
01662     }
01663     // Error variables for QDomDocument::setContent
01664     QString errorMsg;
01665     int errorLine, errorColumn;
01666     bool ok = doc.setContent( store->device(), &errorMsg, &errorLine, &errorColumn );
01667     if ( !ok )
01668     {
01669         kError(30003) << "Parsing error in " << filename << "! Aborting!" << endl
01670             << " In line: " << errorLine << ", column: " << errorColumn << endl
01671             << " Error message: " << errorMsg << endl;
01672         d->lastErrorMessage = i18n( "Parsing error in %1 at line %2, column %3\nError message: %4"
01673                               ,filename  ,errorLine, errorColumn ,
01674                               QCoreApplication::translate("QXml", errorMsg.toUtf8(), 0,
01675                                   QCoreApplication::UnicodeUTF8));
01676         store->close();
01677         return false;
01678     }
01679     kDebug(30003) << "File " << filename << " loaded and parsed" << endl;
01680     return true;
01681 }
01682 
01683 bool KoDocument::loadNativeFormat( const QString & file )
01684 {
01685     QFileInfo fileInfo( file );
01686     if ( !fileInfo.exists() ) // check duplicated from openURL, but this is useful for templates
01687     {
01688         d->lastErrorMessage = i18n("The file %1 does not exist.", file);
01689         return false;
01690     }
01691     if ( !fileInfo.isFile() )
01692     {
01693         d->lastErrorMessage = i18n( "%1 is not a file." , file);
01694         return false;
01695     }
01696 
01697     QApplication::setOverrideCursor( Qt::WaitCursor );
01698 
01699     kDebug(30003) << "KoDocument::loadNativeFormat( " << file << " )" << endl;
01700 
01701     QFile in;
01702     bool isRawXML = false;
01703     if ( d->m_specialOutputFlag != SaveAsDirectoryStore ) // Don't try to open a directory ;)
01704     {
01705         in.setFileName(file);
01706         if ( !in.open( QIODevice::ReadOnly ) )
01707         {
01708             QApplication::restoreOverrideCursor();
01709             d->lastErrorMessage = i18n( "Could not open the file for reading (check read permissions)." );
01710             return false;
01711         }
01712 
01713         char buf[6];
01714         buf[5]=0;
01715         int pos=0;
01716         do {
01717             if ( in.read( buf+pos , 1 ) < 1 )
01718             {
01719                 QApplication::restoreOverrideCursor();
01720                 in.close();
01721                 d->lastErrorMessage = i18n( "Could not read the beginning of the file." );
01722                 return false;
01723             }
01724 
01725             if(QChar( buf[pos] ).isSpace())
01726                 continue;
01727             pos++;
01728         } while ( pos < 5 );
01729         isRawXML = (strncasecmp( buf, "<?xml", 5 ) == 0);
01730         if(! isRawXML)
01731             // also check for broken MathML files, which seem to be rather common
01732             isRawXML = (strncasecmp( buf, "<math", 5 ) == 0); // file begins with <math ?
01733         //kDebug(30003) << "PATTERN=" << buf << endl;
01734     }
01735     // Is it plain XML?
01736     if ( isRawXML )
01737     {
01738         in.seek( 0 );
01739         QString errorMsg;
01740         int errorLine;
01741         int errorColumn;
01742         KoXmlDocument doc;
01743         bool res;
01744         if ( doc.setContent( &in, &errorMsg, &errorLine, &errorColumn ) )
01745         {
01746             res = loadXML( &in, doc );
01747             if ( res )
01748                 res = completeLoading( 0L );
01749         }
01750         else
01751         {
01752             kError (30003) << "Parsing Error! Aborting! (in KoDocument::loadNativeFormat (QFile))" << endl
01753                             << "  Line: " << errorLine << " Column: " << errorColumn << endl
01754                             << "  Message: " << errorMsg << endl;
01755             d->lastErrorMessage = i18n( "parsing error in the main document at line %1, column %2\nError message: %3", errorLine, errorColumn, i18n ( errorMsg.toUtf8() ) );
01756             res=false;
01757         }
01758 
01759         QApplication::restoreOverrideCursor();
01760         in.close();
01761         m_bEmpty = false;
01762         return res;
01763     } else
01764     { // It's a koffice store (tar.gz, zip, directory, etc.)
01765         in.close();
01766 
01767         return loadNativeFormatFromStore( file );
01768     }
01769 }
01770 
01771 bool KoDocument::loadNativeFormatFromStore( const QString& file )
01772 {
01773   KoStore::Backend backend = (d->m_specialOutputFlag == SaveAsDirectoryStore) ? KoStore::Directory : KoStore::Auto;
01774   KoStore * store = KoStore::createStore( file, KoStore::Read, "", backend );
01775 
01776   if ( store->bad() )
01777   {
01778     d->lastErrorMessage = i18n( "Not a valid KOffice file: %1", file );
01779     delete store;
01780     QApplication::restoreOverrideCursor();
01781     return false;
01782   }
01783 
01784   return loadNativeFormatFromStoreInternal( store );
01785 }
01786 
01787 bool KoDocument::loadNativeFormatFromStore( QByteArray &data )
01788 {
01789   KoStore::Backend backend = (d->m_specialOutputFlag == SaveAsDirectoryStore) ? KoStore::Directory : KoStore::Auto;
01790   QBuffer buffer( &data );
01791   KoStore * store = KoStore::createStore( &buffer, KoStore::Read, "", backend );
01792 
01793   if ( store->bad() )
01794     return false;
01795 
01796   return loadNativeFormatFromStoreInternal( store );
01797 }
01798 
01799 bool KoDocument::loadNativeFormatFromStoreInternal( KoStore * store )
01800 {
01801     bool oasis = true;
01802     // OASIS/OOo file format?
01803     if ( store->hasFile( "content.xml" ) )
01804     {
01805         store->disallowNameExpansion();
01806 
01807         KoOasisStore oasisStore( store );
01808         // We could check the 'mimetype' file, but let's skip that and be tolerant.
01809 
01810         if ( !loadOasisFromStore( store ) ) {
01811             delete store;
01812             QApplication::restoreOverrideCursor();
01813             return false;
01814         }
01815 
01816     } else if ( store->hasFile( "root" ) ) // Fallback to "old" file format (maindoc.xml)
01817     {
01818         oasis = false;
01819 
01820         KoXmlDocument doc;
01821         bool ok = oldLoadAndParse( store, "root", doc );
01822         if ( ok )
01823             ok = loadXML( store->device(), doc );
01824         if ( !ok )
01825         {
01826             delete store;
01827             QApplication::restoreOverrideCursor();
01828             return false;
01829         }
01830         store->close();
01831 
01832         if ( !loadChildren( store ) )
01833         {
01834             kError(30003) << "ERROR: Could not load children" << endl;
01835             // Don't abort, proceed nonetheless
01836         }
01837 
01838     } else
01839     {
01840         kError(30003) << "ERROR: No maindoc.xml" << endl;
01841         d->lastErrorMessage = i18n( "Invalid document: no file 'maindoc.xml'." );
01842         delete store;
01843         QApplication::restoreOverrideCursor();
01844         return false;
01845     }
01846 
01847     if ( oasis && store->hasFile( "meta.xml" ) ) {
01848         KoXmlDocument metaDoc;
01849         KoOasisStore oasisStore( store );
01850         if ( oasisStore.loadAndParse( "meta.xml", metaDoc, d->lastErrorMessage ) ) {
01851             d->m_docInfo->loadOasis( metaDoc );
01852         }
01853     }
01854     else if ( !oasis && store->hasFile( "documentinfo.xml" ) )
01855     {
01856         KoXmlDocument doc;
01857         if ( oldLoadAndParse( store, "documentinfo.xml", doc ) ) {
01858             store->close();
01859             d->m_docInfo->load( doc );
01860         }
01861     }
01862     else
01863     {
01864         //kDebug( 30003 ) << "cannot open document info" << endl;
01865         delete d->m_docInfo;
01866         d->m_docInfo = new KoDocumentInfo( this );
01867     }
01868 
01869     if ( oasis && store->hasFile( "VersionList.xml" ) ) {
01870         KMessageBox::information( 0, i18n("This document contains several versions. Go to File->Versions to open an old version."), QString(), "informVersionsAtLoading" );
01871         KoXmlDocument versionInfo;
01872         KoOasisStore oasisStore( store );
01873         if ( oasisStore.loadAndParse( "VersionList.xml", versionInfo, d->lastErrorMessage ) )
01874         {
01875             KoXmlNode list = KoDom::namedItemNS( versionInfo, KoXmlNS::VL, "version-list" );
01876             KoXmlElement e;
01877             forEachElement( e, list )
01878             {
01879                 if ( e.localName() == "version-entry" && e.namespaceURI() == KoXmlNS::VL )
01880                 {
01881                     KoVersionInfo version;
01882                     version.comment = e.attribute( "comment");
01883                     version.title = e.attribute( "title");
01884                     version.saved_by = e.attribute( "creator");
01885                     version.date = QDateTime::fromString( e.attribute( "date-time"), Qt::ISODate );
01886                     store->extractFile( "Versions/"+version.title, version.data );
01887                     d->m_versionInfo.append( version );
01888                 }
01889             }
01890         }
01891     }
01892 
01893     bool res = completeLoading( store );
01894     delete store;
01895     QApplication::restoreOverrideCursor();
01896     m_bEmpty = false;
01897     return res;
01898 }
01899 
01900 // For embedded documents
01901 bool KoDocument::loadFromStore( KoStore* _store, const QString& url )
01902 {
01903     if ( _store->open( url ) )
01904     {
01905         KoXmlDocument doc;
01906         doc.setContent( _store->device() );
01907         if ( !loadXML( _store->device(), doc ) )
01908         {
01909             _store->close();
01910             return false;
01911         }
01912         _store->close();
01913     } else {
01914         kWarning() << "couldn't open " << url << endl;
01915     }
01916 
01917     _store->pushDirectory();
01918     // Store as document URL
01919     if ( url.startsWith( STORE_PROTOCOL ) ) {
01920         m_url = KUrl( url );
01921     } else {
01922         m_url = KUrl( INTERNAL_PREFIX + url );
01923         _store->enterDirectory( url );
01924     }
01925 
01926     if ( !loadChildren( _store ) )
01927     {
01928         kError(30003) << "ERROR: Could not load children" << endl;
01929 #if 0
01930         return false;
01931 #endif
01932     }
01933 
01934     bool result = completeLoading( _store );
01935 
01936     // Restore the "old" path
01937     _store->popDirectory();
01938 
01939     return result;
01940 }
01941 
01942 bool KoDocument::loadOasisFromStore( KoStore* store )
01943 {
01944     KoOasisStyles oasisStyles;
01945     KoXmlDocument contentDoc;
01946     KoXmlDocument settingsDoc;
01947     KoOasisStore oasisStore( store );
01948     bool ok = oasisStore.loadAndParse( "content.xml", contentDoc, d->lastErrorMessage );
01949     if ( !ok )
01950         return false;
01951 
01952     KoXmlDocument stylesDoc;
01953     (void)oasisStore.loadAndParse( "styles.xml", stylesDoc, d->lastErrorMessage );
01954     // Load styles from style.xml
01955     oasisStyles.createStyleMap( stylesDoc, true );
01956     // Also load styles from content.xml
01957     oasisStyles.createStyleMap( contentDoc, false );
01958 
01959     // TODO post 1.4, pass manifestDoc to the apps so that they don't have to do it themselves
01960     // (when calling KoDocumentChild::loadOasisDocument)
01961     //QDomDocument manifestDoc;
01962     //KoOasisStore oasisStore( store );
01963     //if ( !oasisStore.loadAndParse( "tar:/META-INF/manifest.xml", manifestDoc, d->lastErrorMessage ) )
01964     //    return false;
01965 
01966     if ( store->hasFile( "settings.xml" ) ) {
01967         (void)oasisStore.loadAndParse( "settings.xml", settingsDoc, d->lastErrorMessage );
01968     }
01969     if ( !loadOasis( contentDoc, oasisStyles, settingsDoc, store ) )
01970         return false;
01971 
01972     return true;
01973 }
01974 
01975 bool KoDocument::addVersion( const QString& comment )
01976 {
01977     kDebug(30003) << "Saving the new version...." << endl;
01978 
01979     KoStore::Backend backend = KoStore::Auto;
01980     if ( d->m_specialOutputFlag != 0 )
01981       return false;
01982 
01983     QByteArray mimeType = d->outputMimeType;
01984     QByteArray nativeOasisMime = nativeOasisMimeType();
01985     bool oasis = !mimeType.isEmpty() && ( mimeType == nativeOasisMime || mimeType == nativeOasisMime + "-template" );
01986 
01987     if ( !oasis)
01988         return false;
01989 
01990     // TODO: use std::auto_ptr or create store on stack [needs API fixing],
01991     // to remove all the 'delete store' in all the branches
01992     QByteArray data;
01993     QBuffer buffer( &data );
01994     KoStore* store = KoStore::createStore( &buffer/*file*/, KoStore::Write, mimeType, backend );
01995     if ( store->bad() )
01996     {
01997         delete store;
01998         return false;
01999     }
02000 
02001     kDebug(30003) << "Saving to OASIS format" << endl;
02002     // Tell KoStore not to touch the file names
02003     store->disallowNameExpansion();
02004     KoOasisStore oasisStore( store );
02005     KoXmlWriter* manifestWriter = oasisStore.manifestWriter( mimeType );
02006 
02007     if ( !saveOasis( store, manifestWriter ) )
02008     {
02009       kDebug(30003) << "saveOasis failed" << endl;
02010       delete store;
02011       return false;
02012     }
02013 
02014     // Save embedded objects
02015     if ( !saveChildrenOasis( store, manifestWriter ) )
02016     {
02017       kDebug(30003) << "saveChildrenOasis failed" << endl;
02018       delete store;
02019       return false;
02020     }
02021 
02022     // Write out manifest file
02023     if ( !oasisStore.closeManifestWriter() )
02024     {
02025       d->lastErrorMessage = i18n( "Error while trying to write '%1'. Partition full?", QString("META-INF/manifest.xml") );
02026       delete store;
02027       return false;
02028     }
02029 
02030     delete store;
02031 
02032     if ( !saveExternalChildren() )
02033         return false;
02034 
02035     KoVersionInfo version;
02036     version.comment = comment;
02037     version.title = "Version" + QString::number( d->m_versionInfo.count()+1);
02038     version.saved_by = documentInfo()->authorInfo("creator");
02039     version.date = QDateTime::currentDateTime();
02040     version.data = data;
02041     d->m_versionInfo.append( version );
02042 
02043     save(); //finally save the document + the new version
02044     return true;
02045 }
02046 
02047 bool KoDocument::isInOperation() const
02048 {
02049     return d->m_numOperations > 0;
02050 }
02051 
02052 void KoDocument::emitBeginOperation()
02053 {
02054 
02055     /* if we're already in an operation, don't send the signal again */
02056     if (!isInOperation())
02057         emit sigBeginOperation();
02058     d->m_numOperations++;
02059 }
02060 
02061 void KoDocument::emitEndOperation()
02062 {
02063     d->m_numOperations--;
02064 
02065     /* don't end the operation till we've cleared all the nested operations */
02066     if (d->m_numOperations == 0)
02067         emit sigEndOperation();
02068     else if (d->m_numOperations < 0)
02069         /* ignore 'end' calls with no matching 'begin' call */
02070         d->m_numOperations = 0;
02071 }
02072 
02073 
02074 bool KoDocument::isStoredExtern() const
02075 {
02076     return !storeInternal() && hasExternURL();
02077 }
02078 
02079 void KoDocument::setModified( bool mod )
02080 {
02081     if ( isAutosaving() ) // ignore setModified calls due to autosaving
02082         return;
02083 
02084     //kDebug(30003)<<k_funcinfo<<" url:" << m_url.path() << endl;
02085     //kDebug(30003)<<k_funcinfo<<" mod="<<mod<<" MParts mod="<<KParts::ReadWritePart::isModified()<<" isModified="<<isModified()<<endl;
02086 
02087     if ( mod && !d->modifiedAfterAutosave ) {
02088         // First change since last autosave -> start the autosave timer
02089         setAutoSave( d->m_autoSaveDelay );
02090     }
02091     d->modifiedAfterAutosave = mod;
02092 
02093     if ( mod == isModified() )
02094         return;
02095 
02096     KParts::ReadWritePart::setModified( mod );
02097 
02098     if ( mod ) {
02099         m_bEmpty = false;
02100     } else {
02101         // When saving this document, all non-external child documents get saved too.
02102         Q3PtrListIterator<KoDocumentChild> it = children();
02103         for (; it.current(); ++it )
02104         {
02105             KoDocument *doc = it.current()->document();
02106             if ( doc && !it.current()->isStoredExtern() && !it.current()->isDeleted() && doc->isModified() )
02107                 doc->setModified( false );
02108         }
02109     }
02110 
02111     // This influences the title
02112     setTitleModified();
02113     emit modified( mod );
02114 }
02115 
02116 void KoDocument::setDoNotSaveExtDoc( bool on )
02117 {
02118     d->m_doNotSaveExtDoc = on;
02119 }
02120 
02121 int KoDocument::queryCloseDia()
02122 {
02123     //kDebug(30003)<<k_funcinfo<<endl;
02124 
02125     QString name;
02126     if ( documentInfo() )
02127     {
02128         name = documentInfo()->aboutInfo( "title" );
02129     }
02130     if ( name.isEmpty() )
02131         name = url().fileName();
02132 
02133     if ( name.isEmpty() )
02134         name = i18n( "Untitled" );
02135 
02136     int res = KMessageBox::warningYesNoCancel( 0L,
02137                     i18n( "<p>The document <b>'%1'</b> has been modified.</p><p>Do you want to save it?</p>", name ) );
02138 
02139     switch(res)
02140     {
02141         case KMessageBox::Yes :
02142             setDoNotSaveExtDoc(); // Let save() only save myself and my internal docs
02143             save(); // NOTE: External files always in native format. ###TODO: Handle non-native format
02144             setModified( false ); // Now when queryClose() is called by closeEvent it won't do anything.
02145             break;
02146         case KMessageBox::No :
02147             removeAutoSaveFiles();
02148             setModified( false ); // Now when queryClose() is called by closeEvent it won't do anything.
02149             break;
02150         default : // case KMessageBox::Cancel :
02151             return res; // cancels the rest of the files
02152     }
02153     return res;
02154 }
02155 
02156 int KoDocument::queryCloseExternalChildren()
02157 {
02158     //kDebug(30003)<<k_funcinfo<<" checking for children in: "<<url().url()<<endl;
02159     setDoNotSaveExtDoc(false);
02160     Q3PtrListIterator<KoDocumentChild> it( children() );
02161     for (; it.current(); ++it )
02162     {
02163         if ( !it.current()->isDeleted() )
02164         {
02165             KoDocument *doc = it.current()->document();
02166             if ( doc )
02167             {
02168                 bool foo = doc->isStoredExtern();
02169                 kDebug(36001) << "========== isStoredExtern() returned "
02170                                << foo << " ==========" << endl;
02171 
02172                 if ( foo ) //###TODO: Handle non-native mimetype docs
02173                 {
02174                     {
02175                         kDebug(30003)<<k_funcinfo<<" found modified child: "<<doc->url().url()<<" extern="<<doc->isStoredExtern()<<endl;
02176                         if ( doc->queryCloseDia() == KMessageBox::Cancel )
02177                             return  KMessageBox::Cancel;
02178                     }
02179                 }
02180                 if ( doc->queryCloseExternalChildren() == KMessageBox::Cancel )
02181                     return KMessageBox::Cancel;
02182             }
02183         }
02184     }
02185     return KMessageBox::Ok;
02186 }
02187 
02188 void KoDocument::setTitleModified( const QString caption, bool mod )
02189 {
02190     //kDebug(30003)<<k_funcinfo<<" url: "<<url().url()<<" caption: "<<caption<<" mod: "<<mod<<endl;
02191     KoDocument *doc = dynamic_cast<KoDocument *>( parent() );
02192     if ( doc )
02193     {
02194         doc->setTitleModified( caption, mod );
02195         return;
02196     }
02197     // we must be root doc so update caption in all related windows
02198     Q3PtrListIterator<KoMainWindow> it( d->m_shells );
02199     for (; it.current(); ++it )
02200     {
02201         it.current()->updateCaption();
02202         it.current()->updateReloadFileAction(this);
02203         it.current()->updateVersionsFileAction(this);
02204     }
02205 }
02206 
02207 void KoDocument::setTitleModified()
02208 {
02209     //kDebug(30003)<<k_funcinfo<<" url: "<<url().url()<<" extern: "<<isStoredExtern()<<" current: "<<d->m_current<<endl;
02210     KoDocument *doc = dynamic_cast<KoDocument *>( parent() );
02211     QString caption;
02212     if ( (url().isEmpty() || isStoredExtern()) && d->m_current )
02213     {
02214         // Get caption from document info (title(), in about page)
02215         if ( documentInfo() )
02216         {
02217             caption = documentInfo()->aboutInfo( "title" );
02218         }
02219         if ( caption.isEmpty() )
02220             caption = url().pathOrUrl();             // Fall back to document URL
02221 
02222         //kDebug(30003)<<k_funcinfo<<" url: "<<url().url()<<" caption: "<<caption<<endl;
02223         if ( doc )
02224         {
02225             doc->setTitleModified( caption, isModified() );
02226             return;
02227         }
02228         else
02229         {
02230             // we must be root doc so update caption in all related windows
02231             setTitleModified( caption, isModified() );
02232             return;
02233         }
02234     }
02235     if ( doc )
02236     {
02237         // internal doc or not current doc, so pass on the buck
02238         doc->setTitleModified();
02239     }
02240 }
02241 
02242 bool KoDocument::loadChildren( KoStore* )
02243 {
02244     return true;
02245 }
02246 
02247 bool KoDocument::completeLoading( KoStore* )
02248 {
02249     return true;
02250 }
02251 
02252 bool KoDocument::completeSaving( KoStore* )
02253 {
02254     return true;
02255 }
02256 
02257 QDomDocument KoDocument::createDomDocument( const QString& tagName, const QString& version ) const
02258 {
02259     return createDomDocument( instance()->instanceName(), tagName, version );
02260 }
02261 
02262 //static
02263 QDomDocument KoDocument::createDomDocument( const QString& appName, const QString& tagName, const QString& version )
02264 {
02265     QDomImplementation impl;
02266     QString url = QString("http://www.koffice.org/DTD/%1-%2.dtd").arg(appName).arg(version);
02267     QDomDocumentType dtype = impl.createDocumentType( tagName,
02268                                                       QString("-//KDE//DTD %1 %2//EN").arg(appName).arg(version),
02269                                                       url );
02270     // The namespace URN doesn't need to include the version number.
02271     QString namespaceURN = QString("http://www.koffice.org/DTD/%1").arg(appName);
02272     QDomDocument doc = impl.createDocument( namespaceURN, tagName, dtype );
02273     doc.insertBefore( doc.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" ), doc.documentElement() );
02274     return doc;
02275 }
02276 
02277 KoXmlWriter* KoDocument::createOasisXmlWriter( QIODevice* dev, const char* rootElementName )
02278 {
02279     KoXmlWriter* writer = new KoXmlWriter( dev );
02280     writer->startDocument( rootElementName );
02281     writer->startElement( rootElementName );
02282 
02283     if ( qstrcmp( rootElementName, "VL:version-list" ) == 0 ) {
02284         writer->addAttribute( "xmlns:VL", KoXmlNS::VL );
02285         writer->addAttribute( "xmlns:dc", KoXmlNS::dc );
02286         return writer;
02287     }
02288 
02289     writer->addAttribute( "xmlns:office", KoXmlNS::office );
02290     writer->addAttribute( "xmlns:meta", KoXmlNS::meta );
02291 
02292     if ( qstrcmp( rootElementName, "office:document-meta" ) != 0 ) {
02293         writer->addAttribute( "xmlns:config", KoXmlNS::config );
02294         writer->addAttribute( "xmlns:text", KoXmlNS::text );
02295         writer->addAttribute( "xmlns:table", KoXmlNS::table );
02296         writer->addAttribute( "xmlns:draw", KoXmlNS::draw );
02297         writer->addAttribute( "xmlns:presentation", KoXmlNS::presentation );
02298         writer->addAttribute( "xmlns:dr3d", KoXmlNS::dr3d );
02299         writer->addAttribute( "xmlns:chart", KoXmlNS::chart );
02300         writer->addAttribute( "xmlns:form", KoXmlNS::form );
02301         writer->addAttribute( "xmlns:script", KoXmlNS::script );
02302         writer->addAttribute( "xmlns:style", KoXmlNS::style );
02303         writer->addAttribute( "xmlns:number", KoXmlNS::number );
02304         writer->addAttribute( "xmlns:math", KoXmlNS::math );
02305         writer->addAttribute( "xmlns:svg", KoXmlNS::svg );
02306         writer->addAttribute( "xmlns:fo", KoXmlNS::fo );
02307         writer->addAttribute( "xmlns:koffice", KoXmlNS::koffice );
02308     }
02309     // missing: office:version="1.0"
02310 
02311     writer->addAttribute( "xmlns:dc", KoXmlNS::dc );
02312     writer->addAttribute( "xmlns:xlink", KoXmlNS::xlink );
02313     return writer;
02314 }
02315 
02316 QDomDocument KoDocument::saveXML()
02317 {
02318     kError(30003) << "KoDocument::saveXML not implemented" << endl;
02319     d->lastErrorMessage = i18n( "Internal error: saveXML not implemented" );
02320     return QDomDocument();
02321 }
02322 
02323 KService::Ptr KoDocument::nativeService()
02324 {
02325     if ( !m_nativeService )
02326         m_nativeService = readNativeService( instance() );
02327 
02328     return m_nativeService;
02329 }
02330 
02331 QByteArray KoDocument::nativeFormatMimeType() const
02332 {
02333     KService::Ptr service = const_cast<KoDocument *>(this)->nativeService();
02334     if ( !service )
02335         return QByteArray();
02336     QByteArray nativeMimeType = service->property( "X-KDE-NativeMimeType" ).toString().toLatin1();
02337     if ( nativeMimeType.isEmpty() ) {
02338         // shouldn't happen, let's find out why it happened
02339         if ( !service->serviceTypes().contains( "KOfficePart" ) )
02340             kWarning(30003) << "Wrong desktop file, KOfficePart isn't mentionned" << endl;
02341         else if ( !KServiceType::serviceType( "KOfficePart" ) )
02342             kWarning(30003) << "The KOfficePart service type isn't installed!" << endl;
02343     }
02344     return nativeMimeType;
02345 }
02346 
02347 QByteArray KoDocument::nativeOasisMimeType() const
02348 {
02349     KService::Ptr service = const_cast<KoDocument *>(this)->nativeService();
02350     if ( !service )
02351         return QByteArray();
02352     return service->property( "X-KDE-NativeOasisMimeType" ).toString().toLatin1();
02353 }
02354 
02355 
02356 //static
02357 KService::Ptr KoDocument::readNativeService( KInstance *instance )
02358 {
02359     QString instname = instance ? instance->instanceName() : kapp->instanceName();
02360 
02361     // The new way is: we look for a foopart.desktop in the kde_services dir.
02362     QString servicepartname = instname + "part.desktop";
02363     KService::Ptr service = KService::serviceByDesktopPath( servicepartname );
02364     if ( service )
02365         kDebug(30003) << servicepartname << " found." << endl;
02366     if ( !service )
02367     {
02368         // The old way is kept as fallback for compatibility, but in theory this is really never used anymore.
02369 
02370         // Try by path first, so that we find the global one (which has the native mimetype)
02371         // even if the user created a kword.desktop in ~/.kde/share/applnk or any subdir of it.
02372         // If he created it under ~/.kde/share/applnk/Office/ then no problem anyway.
02373         service = KService::serviceByDesktopPath( QString::fromLatin1("Office/%1.desktop").arg(instname) );
02374     }
02375     if ( !service )
02376         service = KService::serviceByDesktopName( instname );
02377 
02378     return service;
02379 }
02380 
02381 QByteArray KoDocument::readNativeFormatMimeType( KInstance *instance ) //static
02382 {
02383     KService::Ptr service = readNativeService( instance );
02384     if ( !service )
02385         return QByteArray();
02386 
02387     if ( service->property( "X-KDE-NativeMimeType" ).toString().isEmpty() )
02388     {
02389         // It may be that the servicetype "KOfficePart" is missing, which leads to this property not being known
02390                 KServiceType::Ptr ptr = KServiceType::serviceType( "KOfficePart" );
02391         if ( !ptr )
02392             kError(30003) << "The serviceType KOfficePart is missing. Check that you have a kofficepart.desktop file in the share/servicetypes directory." << endl;
02393         else {
02394             QString instname = instance ? instance->instanceName() : kapp->instanceName();
02395             if ( instname != "koshell" ) // hack for koshell
02396                 kWarning(30003) << service->desktopEntryPath() << ": no X-KDE-NativeMimeType entry!" << endl;
02397         }
02398     }
02399 
02400     return service->property( "X-KDE-NativeMimeType" ).toString().toLatin1();
02401 }
02402 
02403 QStringList KoDocument::readExtraNativeMimeTypes( KInstance *instance ) //static
02404 {
02405     KService::Ptr service = readNativeService( instance );
02406     if ( !service )
02407         return QStringList();
02408     return service->property( "X-KDE-ExtraNativeMimeTypes" ).toStringList();
02409 }
02410 
02411 void KoDocument::setupXmlReader( QXmlSimpleReader& reader, bool namespaceProcessing )
02412 {
02413     if ( namespaceProcessing )
02414     {
02415         reader.setFeature( "http://xml.org/sax/features/namespaces", true );
02416         reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", false );
02417     }
02418     else
02419     {
02420         reader.setFeature( "http://xml.org/sax/features/namespaces", false );
02421         reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", true );
02422     }
02423     reader.setFeature( "http://trolltech.com/xml/features/report-whitespace-only-CharData", true );
02424 }
02425 
02426 
02427 bool KoDocument::isNativeFormat( const QByteArray& mimetype ) const
02428 {
02429     if ( mimetype == nativeFormatMimeType() )
02430         return true;
02431     return extraNativeMimeTypes().contains( mimetype );
02432 }
02433 
02434 QStringList KoDocument::extraNativeMimeTypes() const
02435 {
02436     QStringList lst;
02437     // This implementation is temporary while we treat both koffice-1.3 and OASIS formats as native.
02438     // But it's good to have this virtual method, in case some app want to
02439     // support more than one native format.
02440     KService::Ptr service = const_cast<KoDocument *>(this)->nativeService();
02441     if ( !service ) // can't happen
02442         return lst;
02443     return service->property( "X-KDE-ExtraNativeMimeTypes" ).toStringList();
02444 }
02445 
02446 int KoDocument::supportedSpecialFormats() const
02447 {
02448     // Apps which support special output flags can add reimplement and add to this.
02449     // E.g. this is how did "saving in the 1.1 format".
02450     // SaveAsDirectoryStore is a given since it's implemented by KoDocument itself.
02451     return SaveAsDirectoryStore;
02452 }
02453 
02454 void KoDocument::addShell( KoMainWindow *shell )
02455 {
02456     if ( d->m_shells.findRef( shell ) == -1 )
02457     {
02458         //kDebug(30003) << "addShell: shell " << (void*)shell << " added to doc " << this << endl;
02459         d->m_shells.append( shell );
02460     }
02461 }
02462 
02463 void KoDocument::removeShell( KoMainWindow *shell )
02464 {
02465     //kDebug(30003) << "removeShell: shell " << (void*)shell << " removed from doc " << this << endl;
02466     d->m_shells.removeRef( shell );
02467 }
02468 
02469 const Q3PtrList<KoMainWindow>& KoDocument::shells() const
02470 {
02471     return d->m_shells;
02472 }
02473 
02474 int KoDocument::shellCount() const
02475 {
02476     return d->m_shells.count();
02477 }
02478 
02479 // DCOPObject * KoDocument::dcopObject()
02480 // {
02481 //     if ( !d->m_dcopObject )
02482 //         d->m_dcopObject = new KoDocumentIface( this );
02483 //     return d->m_dcopObject;
02484 // }
02485 
02486 // QByteArray KoDocument::dcopObjectId() const
02487 // {
02488 //     return const_cast<KoDocument *>(this)->dcopObject()->objId();
02489 // }
02490 
02491 void KoDocument::setErrorMessage( const QString& errMsg )
02492 {
02493     d->lastErrorMessage = errMsg;
02494 }
02495 
02496 QString KoDocument::errorMessage() const
02497 {
02498     return d->lastErrorMessage;
02499 }
02500 
02501 void KoDocument::showSavingErrorDialog()
02502 {
02503     if ( d->lastErrorMessage.isEmpty() )
02504     {
02505         KMessageBox::error( 0L, i18n( "Could not save\n%1", m_file ) );
02506     }
02507     else if ( d->lastErrorMessage != "USER_CANCELED" )
02508     {
02509         KMessageBox::error( 0L, i18n( "Could not save %1\nReason: %2", m_file, d->lastErrorMessage ) );
02510     }
02511 }
02512 
02513 void KoDocument::showLoadingErrorDialog()
02514 {
02515     if ( d->lastErrorMessage.isEmpty() )
02516     {
02517         KMessageBox::error( 0L, i18n( "Could not open\n%1", url().pathOrUrl() ) );
02518     }
02519     else if ( d->lastErrorMessage != "USER_CANCELED" )
02520     {
02521         KMessageBox::error( 0L, i18n( "Could not open %1\nReason: %2", url().pathOrUrl(), d->lastErrorMessage ) );
02522     }
02523 }
02524 
02525 bool KoDocument::isAutosaving() const
02526 {
02527     return d->m_autosaving;
02528 }
02529 
02530 bool KoDocument::isLoading() const
02531 {
02532     return d->m_bLoading;
02533 }
02534 
02535 void KoDocument::removeAutoSaveFiles()
02536 {
02537         // Eliminate any auto-save file
02538         QString asf = autoSaveFile( m_file ); // the one in the current dir
02539         if ( QFile::exists( asf ) )
02540             QFile::remove( asf );
02541         asf = autoSaveFile( QString::null ); // and the one in $HOME
02542         if ( QFile::exists( asf ) )
02543             QFile::remove( asf );
02544 }
02545 
02546 void KoDocument::setBackupFile( bool _b )
02547 {
02548     d->m_backupFile = _b;
02549 }
02550 
02551 bool KoDocument::backupFile()const
02552 {
02553     return d->m_backupFile;
02554 }
02555 
02556 
02557 void KoDocument::setBackupPath( const QString & _path)
02558 {
02559     d->m_backupPath = _path;
02560 }
02561 
02562 QString KoDocument::backupPath()const
02563 {
02564     return d->m_backupPath;
02565 }
02566 
02567 void KoDocument::setCurrent( bool on )
02568 {
02569     //kDebug(30003)<<k_funcinfo<<" url: "<<url().url()<<" set to: "<<on<<endl;
02570     KoDocument *doc = dynamic_cast<KoDocument *>( parent() );
02571     if ( doc )
02572     {
02573         if ( !isStoredExtern() )
02574         {
02575             // internal doc so set next external to current (for safety)
02576             doc->setCurrent( true );
02577             return;
02578         }
02579         // only externally stored docs shall have file name in title
02580         d->m_current = on;
02581         if ( !on )
02582         {
02583             doc->setCurrent( true );    // let my next external parent take over
02584             return;
02585         }
02586         doc->forceCurrent( false ); // everybody else should keep off
02587     }
02588     else
02589         d->m_current = on;
02590 
02591     setTitleModified();
02592 }
02593 
02594 void KoDocument::forceCurrent( bool on )
02595 {
02596     //kDebug(30003)<<k_funcinfo<<" url: "<<url().url()<<" force to: "<<on<<endl;
02597     d->m_current = on;
02598     KoDocument *doc = dynamic_cast<KoDocument *>( parent() );
02599     if ( doc )
02600     {
02601         doc->forceCurrent( false );
02602     }
02603 }
02604 
02605 bool KoDocument::isCurrent() const
02606 {
02607     return d->m_current;
02608 }
02609 
02610 bool KoDocument::storeInternal() const
02611 {
02612     return d->m_storeInternal;
02613 }
02614 
02615 void KoDocument::setStoreInternal( bool i )
02616 {
02617     d->m_storeInternal = i;
02618     //kDebug(30003)<<k_funcinfo<<"="<<d->m_storeInternal<<" doc: "<<url().url()<<endl;
02619 }
02620 
02621 bool KoDocument::hasExternURL() const
02622 {
02623     return !url().protocol().isEmpty() && url().protocol() != STORE_PROTOCOL && url().protocol() != INTERNAL_PROTOCOL;
02624 }
02625 
02626 void KoDocument::slotStarted( KIO::Job* job )
02627 {
02628     if ( job && job->ui() )
02629     {
02630         job->ui()->setWindow( d->m_shells.current() );
02631     }
02632 }
02633 
02634 static const struct {
02635     const char* localName;
02636     const char* documentType;
02637 } TN2DTArray[] = {
02638     { "text", I18N_NOOP( "a word processing" ) },
02639     { "spreadsheet", I18N_NOOP( "a spreadsheet" ) },
02640     { "presentation", I18N_NOOP( "a presentation" ) },
02641     { "chart", I18N_NOOP( "a chart" ) },
02642     { "drawing", I18N_NOOP( "a drawing" ) }
02643 };
02644 static const unsigned int numTN2DT = sizeof( TN2DTArray ) / sizeof( *TN2DTArray );
02645 
02646 QString KoDocument::tagNameToDocumentType( const QString& localName )
02647 {
02648     for ( unsigned int i = 0 ; i < numTN2DT ; ++i )
02649         if ( localName == TN2DTArray[i].localName )
02650             return i18n( TN2DTArray[i].documentType );
02651     return localName;
02652 }
02653 
02654 QList<KoTextDocument *> KoDocument::allTextDocuments() const
02655 {
02656     return QList<KoTextDocument *>();
02657 }
02658 
02659 KoPageLayout KoDocument::pageLayout(int /*pageNumber*/) const
02660 {
02661     return m_pageLayout;
02662 }
02663 
02664 KoUnit::Unit KoDocument::unit() const
02665 {
02666     return d->m_unit;
02667 }
02668 
02669 void KoDocument::setUnit( KoUnit::Unit unit )
02670 {
02671     if ( d->m_unit != unit )
02672     {
02673         d->m_unit = unit;
02674         emit unitChanged( unit );
02675     }
02676 }
02677 
02678 QString KoDocument::unitName() const
02679 {
02680     return KoUnit::unitName( unit() );
02681 }
02682 
02683 void KoDocument::showStartUpWidget( KoMainWindow* parent, bool alwaysShow )
02684 {
02685     if(!alwaysShow) {
02686         KConfigGroup cfgGrp( instance()->config(), "TemplateChooserDialog" );
02687         QString fullTemplateName = cfgGrp.readPathEntry( "AlwaysUseTemplate" );
02688 
02689         if( !fullTemplateName.isEmpty() ) {
02690             openTemplate( fullTemplateName );
02691             shells().getFirst()->setRootDocument( this );
02692             return;
02693         }
02694     }
02695 
02696     if(d->m_startUpWidget){
02697         d->m_startUpWidget->show();
02698     } else {
02699         d->m_startUpWidget = createOpenPane( parent->centralWidget(), instance(), templateType() );
02700     }
02701 
02702     parent->setDocToOpen( this );
02703     parent->factory()->container("mainToolBar", parent)->hide();
02704 }
02705 
02706 void KoDocument::openExistingFile( const KUrl& url )
02707 {
02708     bool ok = openURL( url );
02709     setModified( false );
02710 
02711     if( ok )
02712         QTimer::singleShot( 0, this, SLOT( deleteOpenPane() ) );
02713 }
02714 
02715 void KoDocument::openTemplate( const KUrl& url )
02716 {
02717     bool ok = loadNativeFormat( url.path() );
02718     setModified( false );
02719 
02720     if ( ok ) {
02721         deleteOpenPane();
02722         resetURL();
02723         setEmpty();
02724     } else {
02725         showLoadingErrorDialog();
02726         initEmpty();
02727     }
02728 }
02729 
02730 void KoDocument::initEmpty()
02731 {
02732     setEmpty();
02733     setModified(false);
02734 }
02735 
02736 void KoDocument::startCustomDocument() {
02737     deleteOpenPane();
02738 }
02739 
02740 KoOpenPane* KoDocument::createOpenPane( QWidget* parent, KInstance* instance,
02741                                         const QString& templateType )
02742 {
02743     KoOpenPane* openPane = new KoOpenPane( parent, instance, templateType );
02744     QWidget *customDoc = createCustomDocumentWidget(openPane);
02745     if(customDoc) {
02746         openPane->setCustomDocumentWidget( customDoc );
02747         connect( customDoc, SIGNAL( documentSelected() ), this, SLOT( startCustomDocument() ) );
02748     }
02749     openPane->show();
02750 
02751     connect( openPane, SIGNAL( openExistingFile( const KUrl& ) ),
02752              this, SLOT( openExistingFile( const KUrl& ) ) );
02753     connect( openPane, SIGNAL( openTemplate( const KUrl& ) ),
02754              this, SLOT( openTemplate( const KUrl& ) ) );
02755 
02756     return openPane;
02757 }
02758 
02759 void KoDocument::setTemplateType( const QString& _templateType )
02760 {
02761     d->m_templateType = _templateType;
02762 }
02763 
02764 QString KoDocument::templateType() const
02765 {
02766     return d->m_templateType;
02767 }
02768 
02769 void KoDocument::deleteOpenPane()
02770 {
02771     if( d->m_startUpWidget ) {
02772         d->m_startUpWidget->hide();
02773         QTimer::singleShot(1000, this, SLOT(deleteOpenPaneDelayed()));
02774 
02775         shells().getFirst()->factory()->container("mainToolBar", shells().getFirst())->show();
02776         shells().getFirst()->setRootDocument( this );
02777     } else {
02778         emit closeEmbedInitDialog();
02779     }
02780 }
02781 
02782 void KoDocument::deleteOpenPaneDelayed()
02783 {
02784     delete d->m_startUpWidget;
02785     d->m_startUpWidget = 0;
02786 }
02787 
02788 QWidget* KoDocument::createCustomDocumentWidget(QWidget */*parent*/) {
02789     return 0;
02790 }
02791 
02792 bool KoDocument::showEmbedInitDialog(QWidget* parent)
02793 {
02794     KDialog dlg( parent );
02795     dlg.setCaption( i18n("Embedding Object") );
02796     KoOpenPane* pane = createOpenPane(&dlg, instance(), templateType());
02797     pane->layout()->setMargin(0);
02798     dlg.setMainWidget(pane);
02799     KConfig cfg("EmbedInitDialog");
02800     /*dlg.setInitialSize(*/dlg.restoreDialogSize( &cfg /*)*/);
02801     connect(this, SIGNAL(closeEmbedInitDialog()), &dlg, SLOT(slotOk()));
02802 
02803     bool ok = dlg.exec() == QDialog::Accepted;
02804 
02805     dlg.saveDialogSize(&cfg);
02806 
02807     return ok;
02808 }
02809 
02810 QList<KoVersionInfo> & KoDocument::versionList()
02811 {
02812   return d->m_versionInfo;
02813 }
02814 
02815 #include "KoDocument_p.moc"
02816 #include "KoDocument.moc"

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