lib

KoDocument.cpp

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