~ubuntu-branches/ubuntu/breezy/koffice/breezy

« back to all changes in this revision

Viewing changes to lib/kofficecore/koDocument.cc

  • Committer: Bazaar Package Importer
  • Author(s): Ben Burton
  • Date: 2004-05-09 11:33:00 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20040509113300-vfrdadqsvjfuhn3b
Tags: 1:1.3.1-1
* New upstream bugfix release.
* Built against newer imagemagick (closes: #246623).
* Made koffice-libs/kformula recommend/depend on latex-xft-fonts, which
  provides mathematical fonts that the formula editor can use.  Also
  patched the kformula part to make these fonts the default.
* Changed kword menu hint from "WordProcessors" to "Word processors"
  (closes: #246209).
* Spellchecker configuration is now fixed (closes: #221256, #227568).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/* This file is part of the KDE project
2
2
   Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
 
3
   Copyright (C) 2000, 2001 David Faure <david@mandrakesoft.com>
3
4
 
4
5
   This library is free software; you can redistribute it and/or
5
6
   modify it under the terms of the GNU Library General Public
17
18
   Boston, MA 02111-1307, USA.
18
19
*/
19
20
 
 
21
#ifdef HAVE_CONFIG_H
20
22
#include <config.h>
 
23
#endif
21
24
 
22
25
#include <assert.h>
23
26
#include <unistd.h>
24
27
 
 
28
#include <qbuffer.h>
 
29
 
25
30
#include <koDocument.h>
26
31
#include <koDocument_p.h>
27
32
#include <KoDocumentIface.h>
28
33
#include <koDocumentChild.h>
29
34
#include <koView.h>
30
 
#include <koApplication.h>
31
35
#include <koMainWindow.h>
32
36
#include <koStoreDevice.h>
33
 
#include <koQueryTrader.h>
34
37
#include <koFilterManager.h>
35
38
#include <koDocumentInfo.h>
 
39
#include <kprinter.h>
36
40
 
37
 
#include <koStore.h>
38
41
#include <kio/netaccess.h>
39
 
 
 
42
#include <kio/job.h>
 
43
#include <kparts/partmanager.h>
40
44
#include <klocale.h>
41
45
#include <kmimetype.h>
42
 
#include <kapp.h>
 
46
#include <kapplication.h>
43
47
#include <kdebug.h>
44
48
#include <kmessagebox.h>
 
49
#include <kdeversion.h>
 
50
#if ! KDE_IS_VERSION(3,1,90)
 
51
#include <kdebugclasses.h>
 
52
#include <kfileitem.h>
 
53
#endif
45
54
 
46
55
#include <qfile.h>
 
56
#include <qmap.h>
47
57
#include <qpainter.h>
48
 
#include <qcolor.h>
49
 
#include <qpicture.h>
50
 
#include <qdom.h>
51
 
#include <qtextstream.h>
52
58
#include <qtimer.h>
 
59
#include <qimage.h>
 
60
#include <kiconloader.h>
 
61
#include <qdir.h>
 
62
#include <qfileinfo.h>
 
63
#include <qcursor.h>
53
64
 
54
65
// Define the protocol used here for embedded documents' URL
55
66
// This used to "store" but KURL didn't like it,
56
67
// so let's simply make it "tar" !
57
68
#define STORE_PROTOCOL "tar"
58
 
#define STORE_PROTOCOL_LENGTH 3
59
 
// Warning, keep it sync in koStore.cc
 
69
// The internal path is a hack to make KURL happy and still pass
 
70
// some kind of relative path to KoDocumentChild
 
71
#define INTERNAL_PROTOCOL "intern"
 
72
#define INTERNAL_PREFIX "intern:/"
 
73
// Warning, keep it sync in koStore.cc and koDocumentChild.cc
60
74
 
61
 
QList<KoDocument> *KoDocument::s_documentList=0L;
 
75
QPtrList<KoDocument> *KoDocument::s_documentList=0L;
62
76
 
63
77
using namespace std;
64
78
class KoViewWrapperWidget;
69
83
 *
70
84
 **********************************************************/
71
85
 
72
 
class KoDocumentPrivate
 
86
const int KoDocument::s_defaultAutoSave = 300; // 5 minutes
 
87
 
 
88
class KoDocument::Private
73
89
{
74
90
public:
75
 
    KoDocumentPrivate()
76
 
    {
77
 
    }
78
 
    ~KoDocumentPrivate()
79
 
    {
80
 
    }
81
 
 
82
 
    QList<KoView> m_views;
83
 
    QList<KoDocumentChild> m_children;
84
 
    QList<KoMainWindow> m_shells;
85
 
 
86
 
    bool m_bSingleViewMode;
87
 
    mutable bool m_changed;
 
91
    Private() :
 
92
        m_dcopObject( 0L ),
 
93
        filterManager( 0L ),
 
94
        m_specialOutputFlag( 0 ),
 
95
        m_isImporting( false ), m_isExporting( false ),
 
96
        m_numOperations( 0 ),
 
97
        modifiedAfterAutosave( false ),
 
98
        m_autosaving( false ),
 
99
        m_shouldCheckAutoSaveFile( true ),
 
100
        m_autoErrorHandlingEnabled( true ),
 
101
        m_backupFile( true ),
 
102
        m_backupPath( QString::null ),
 
103
        m_doNotSaveExtDoc( false ),
 
104
        m_current( false ),
 
105
        m_storeInternal( false ),
 
106
        m_initDocFlags( KoDocument::InitDocAppStarting )
 
107
    {
 
108
        m_confirmNonNativeSave[0] = true;
 
109
        m_confirmNonNativeSave[1] = true;
 
110
    }
 
111
 
 
112
    QPtrList<KoView> m_views;
 
113
    QPtrList<KoDocumentChild> m_children;
 
114
    QPtrList<KoMainWindow> m_shells;
 
115
    QValueList<QDomDocument> m_viewBuildDocuments;
88
116
 
89
117
    KoViewWrapperWidget *m_wrapperWidget;
90
 
 
91
 
    QValueList<QDomDocument> m_viewBuildDocuments;
92
118
    KoDocumentIface * m_dcopObject;
93
 
 
94
119
    KoDocumentInfo *m_docInfo;
95
120
 
 
121
    KoFilterManager * filterManager; // The filter-manager to use when loading/saving [for the options]
 
122
 
 
123
    QCString mimeType; // The actual mimetype of the document
96
124
    QCString outputMimeType; // The mimetype to use when saving
97
 
    KoFilterManager * filterManager; // The filter-manager to use when loading/saving [for the options]
98
 
    // It can be set by KoMainWindow (to pass options), or created here (without options)
 
125
    bool m_confirmNonNativeSave [2]; // used to pop up a dialog when saving for the
 
126
                                     // first time if the file is in a foreign format
 
127
                                     // (Save/Save As, Export)
 
128
    int m_specialOutputFlag; // See KoFileDialog in koMainWindow.cc
 
129
    bool m_isImporting, m_isExporting; // File --> Import/Export vs File --> Open/Save
99
130
 
100
131
    QTimer m_autoSaveTimer;
 
132
    QString lastErrorMessage; // see openFile()
101
133
    int m_autoSaveDelay; // in seconds, 0 to disable.
 
134
    int m_numOperations;
102
135
    bool modifiedAfterAutosave;
 
136
    bool m_bSingleViewMode;
 
137
    bool m_autosaving;
 
138
    bool m_shouldCheckAutoSaveFile; // usually true
 
139
    bool m_autoErrorHandlingEnabled; // usually true
 
140
    bool m_backupFile;
 
141
    QString m_backupPath;
 
142
    bool m_doNotSaveExtDoc; // makes it possible to save only internally stored child documents
 
143
    bool m_current;
 
144
    bool m_storeInternal; // Store this doc internally even if url is external
 
145
    InitDocFlags m_initDocFlags;
103
146
};
104
147
 
105
148
// Used in singleViewMode
106
149
class KoViewWrapperWidget : public QWidget
107
150
{
108
151
public:
109
 
  KoViewWrapperWidget( QWidget *parent, const char *name )
110
 
    : QWidget( parent, name )
111
 
  {
112
 
    KGlobal::locale()->insertCatalogue("koffice");
113
 
    // Tell the iconloader about share/apps/koffice/icons
114
 
    KGlobal::iconLoader()->addAppDir("koffice");
115
 
    m_view = 0L;
116
 
  }
117
 
 
118
 
  virtual ~KoViewWrapperWidget() {}
119
 
 
120
 
  virtual void resizeEvent( QResizeEvent * )
121
 
  {
122
 
    QObject *wid = child( 0, "QWidget" );
123
 
    if ( wid )
124
 
      static_cast<QWidget *>(wid)->setGeometry( 0, 0, width(), height() );
125
 
  }
126
 
 
127
 
  virtual void childEvent( QChildEvent *ev )
128
 
  {
129
 
    if ( ev->type() == QEvent::ChildInserted )
130
 
      resizeEvent( 0L );
131
 
  }
132
 
 
133
 
  void setKoView( KoView * view ) { m_view = view; }
134
 
  KoView * koView() const { return m_view; }
 
152
    KoViewWrapperWidget( QWidget *parent, const char *name )
 
153
        : QWidget( parent, name )
 
154
    {
 
155
        KGlobal::locale()->insertCatalogue("koffice");
 
156
        // Tell the iconloader about share/apps/koffice/icons
 
157
        KGlobal::iconLoader()->addAppDir("koffice");
 
158
        m_view = 0L;
 
159
        // Avoid warning from KParts - we'll have the KoView as focus proxy anyway
 
160
        setFocusPolicy( ClickFocus );
 
161
    }
 
162
 
 
163
    virtual ~KoViewWrapperWidget() {
 
164
        setFocusProxy( 0 ); // to prevent a crash due to clearFocus (#53466)
 
165
    }
 
166
 
 
167
    virtual void resizeEvent( QResizeEvent * )
 
168
    {
 
169
        QObject *wid = child( 0, "QWidget" );
 
170
        if ( wid )
 
171
            static_cast<QWidget *>(wid)->setGeometry( 0, 0, width(), height() );
 
172
    }
 
173
 
 
174
    virtual void childEvent( QChildEvent *ev )
 
175
    {
 
176
        if ( ev->type() == QEvent::ChildInserted )
 
177
            resizeEvent( 0L );
 
178
    }
 
179
 
 
180
    // Called by openFile()
 
181
    void setKoView( KoView * view ) {
 
182
        m_view = view;
 
183
        setFocusProxy( m_view );
 
184
    }
 
185
    KoView * koView() const { return m_view; }
135
186
private:
136
 
  KoView* m_view;
 
187
    KoView* m_view;
137
188
};
138
189
 
139
190
KoBrowserExtension::KoBrowserExtension( KoDocument * doc, const char * name )
153
204
    view->setupPrinter( printer );
154
205
    if ( printer.setup( view ) )
155
206
        view->print( printer );
156
 
};
 
207
}
157
208
 
158
209
KoDocument::KoDocument( QWidget * parentWidget, const char *widgetName, QObject* parent, const char* name, bool singleViewMode )
159
210
    : KParts::ReadWritePart( parent, name )
160
211
{
161
 
  if(s_documentList==0L)
162
 
    s_documentList=new QList<KoDocument>;
163
 
  s_documentList->append(this);
164
 
 
165
 
  d = new KoDocumentPrivate;
166
 
  m_bEmpty = TRUE;
167
 
  d->m_changed=false;
168
 
  d->m_dcopObject = 0L;
169
 
  connect( &d->m_autoSaveTimer, SIGNAL( timeout() ), this, SLOT( slotAutoSave() ) );
170
 
  setAutoSave( s_defaultAutoSave );
171
 
  d->m_bSingleViewMode = singleViewMode;
172
 
  d->filterManager = 0L;
173
 
  d->modifiedAfterAutosave=false;
174
 
 
175
 
  // the parent setting *always* overrides! (Simon)
176
 
  if ( parent )
177
 
  {
178
 
    if ( parent->inherits( "KoDocument" ) )
179
 
      d->m_bSingleViewMode = ((KoDocument *)parent)->isSingleViewMode();
180
 
    else if ( parent->inherits( "KParts::Part" ) )
181
 
      d->m_bSingleViewMode = true;
182
 
  }
183
 
 
184
 
  if ( singleViewMode )
185
 
  {
186
 
      d->m_wrapperWidget = new KoViewWrapperWidget( parentWidget, widgetName );
187
 
      setWidget( d->m_wrapperWidget );
188
 
      kdDebug(30003) << "creating KoBrowserExtension" << endl;
189
 
      (void) new KoBrowserExtension( this ); // ## only if embedded into a browser?
190
 
    }
191
 
 
192
 
  d->m_docInfo = new KoDocumentInfo( this, "document info" );
 
212
    if(s_documentList==0L)
 
213
        s_documentList=new QPtrList<KoDocument>;
 
214
    s_documentList->append(this);
 
215
 
 
216
    d = new Private;
 
217
    m_bEmpty = TRUE;
 
218
    connect( &d->m_autoSaveTimer, SIGNAL( timeout() ), this, SLOT( slotAutoSave() ) );
 
219
    setAutoSave( s_defaultAutoSave );
 
220
    d->m_bSingleViewMode = singleViewMode;
 
221
 
 
222
 
 
223
    // the parent setting *always* overrides! (Simon)
 
224
    if ( parent )
 
225
    {
 
226
        if ( parent->inherits( "KoDocument" ) )
 
227
            d->m_bSingleViewMode = ((KoDocument *)parent)->isSingleViewMode();
 
228
        else if ( parent->inherits( "KParts::Part" ) )
 
229
            d->m_bSingleViewMode = true;
 
230
    }
 
231
 
 
232
    if ( singleViewMode )
 
233
    {
 
234
        d->m_wrapperWidget = new KoViewWrapperWidget( parentWidget, widgetName );
 
235
        setWidget( d->m_wrapperWidget );
 
236
        kdDebug(30003) << "creating KoBrowserExtension" << endl;
 
237
        (void) new KoBrowserExtension( this ); // ## only if embedded into a browser?
 
238
    }
 
239
 
 
240
    d->m_docInfo = new KoDocumentInfo( this, "document info" );
 
241
 
 
242
    m_pageLayout.ptWidth = 0;
 
243
    m_pageLayout.ptHeight = 0;
 
244
    m_pageLayout.ptTop = 0;
 
245
    m_pageLayout.ptBottom = 0;
 
246
    m_pageLayout.ptLeft = 0;
 
247
    m_pageLayout.ptRight = 0;
 
248
 
 
249
    // A way to 'fix' the job's window, since we have no widget known to KParts
 
250
    if ( !singleViewMode )
 
251
        connect( this, SIGNAL( started( KIO::Job* ) ), SLOT( slotStarted( KIO::Job* ) ) );
193
252
}
194
253
 
195
254
KoDocument::~KoDocument()
196
255
{
197
 
  QListIterator<KoDocumentChild> childIt( d->m_children );
198
 
  for (; childIt.current(); ++childIt )
199
 
    disconnect( childIt.current(), SIGNAL( destroyed() ),
200
 
                this, SLOT( slotChildDestroyed() ) );
201
 
 
202
 
  // Tell our views that the document is already destroyed and
203
 
  // that they shouldn't try to access it.
204
 
  QListIterator<KoView> vIt( d->m_views );
205
 
  for (; vIt.current(); ++vIt )
206
 
      vIt.current()->setDocumentDeleted();
207
 
 
208
 
  d->m_children.setAutoDelete( true );
209
 
  d->m_children.clear();
210
 
 
211
 
  d->m_shells.setAutoDelete( true );
212
 
  d->m_shells.clear();
213
 
 
214
 
  delete d->m_dcopObject;
215
 
  delete d;
216
 
  s_documentList->removeRef(this);
217
 
  // last one?
218
 
  if(s_documentList->count()==0) {
219
 
      delete s_documentList;
220
 
      s_documentList=0;
221
 
  }
 
256
    d->m_autoSaveTimer.stop();
 
257
 
 
258
    QPtrListIterator<KoDocumentChild> childIt( d->m_children );
 
259
    for (; childIt.current(); ++childIt )
 
260
        disconnect( childIt.current(), SIGNAL( destroyed() ),
 
261
                    this, SLOT( slotChildDestroyed() ) );
 
262
 
 
263
    // Tell our views that the document is already destroyed and
 
264
    // that they shouldn't try to access it.
 
265
    QPtrListIterator<KoView> vIt( d->m_views );
 
266
    for (; vIt.current(); ++vIt )
 
267
        vIt.current()->setDocumentDeleted();
 
268
 
 
269
    d->m_children.setAutoDelete( true );
 
270
    d->m_children.clear();
 
271
 
 
272
    d->m_shells.setAutoDelete( true );
 
273
    d->m_shells.clear();
 
274
 
 
275
    delete d->m_dcopObject;
 
276
    delete d->filterManager;
 
277
    delete d;
 
278
    s_documentList->removeRef(this);
 
279
    // last one?
 
280
    if(s_documentList->isEmpty()) {
 
281
        delete s_documentList;
 
282
        s_documentList=0;
 
283
    }
222
284
}
223
285
 
224
286
bool KoDocument::isSingleViewMode() const
225
287
{
226
 
  return d->m_bSingleViewMode;
227
 
}
228
 
 
229
 
bool KoDocument::isEmbedded() const {
230
 
  return dynamic_cast<KoDocument *>( parent() ) != 0;
231
 
}
232
 
 
233
 
KoView *KoDocument::createView( QWidget *parent, const char *name ) {
234
 
 
 
288
    return d->m_bSingleViewMode;
 
289
}
 
290
 
 
291
bool KoDocument::isEmbedded() const
 
292
{
 
293
    return dynamic_cast<KoDocument *>( parent() ) != 0;
 
294
}
 
295
 
 
296
KoView *KoDocument::createView( QWidget *parent, const char *name )
 
297
{
235
298
    KoView *view=createViewInstance(parent, name);
236
299
    addView(view);
237
300
    return view;
238
301
}
239
302
 
 
303
bool KoDocument::exp0rt( const KURL & _url )
 
304
{
 
305
    bool ret;
 
306
 
 
307
    d->m_isExporting = true;
 
308
 
 
309
    //
 
310
    // Preserve a lot of state here because we need to restore it in order to
 
311
    // be able to fake a File --> Export.  Can't do this in saveFile() because,
 
312
    // for a start, KParts has already set m_url and m_file and because we need
 
313
    // to restore the modified flag etc. and don't want to put a load on anyone
 
314
    // reimplementing saveFile() (Note: import() and export() will remain
 
315
    // non-virtual).
 
316
    //
 
317
    KURL oldURL = m_url;
 
318
    QString oldFile = m_file;
 
319
 
 
320
    bool wasModified = isModified ();
 
321
    QCString oldMimeType = mimeType ();
 
322
 
 
323
 
 
324
    // save...
 
325
    ret = saveAs( _url );
 
326
 
 
327
 
 
328
    //
 
329
    // This is sooooo hacky :(
 
330
    // Hopefully we will restore enough state.
 
331
    //
 
332
    kdDebug(30003) << "Restoring KoDocument state to before export" << endl;
 
333
 
 
334
    // always restore m_url & m_file because KParts has changed them
 
335
    // (regardless of failure or success)
 
336
    m_url = oldURL;
 
337
    m_file = oldFile;
 
338
 
 
339
    // on successful export we need to restore modified etc. too
 
340
    // on failed export, mimetype/modified hasn't changed anyway
 
341
    if (ret)
 
342
    {
 
343
        setModified (wasModified);
 
344
        d->mimeType = oldMimeType;
 
345
    }
 
346
 
 
347
 
 
348
    d->m_isExporting = false;
 
349
 
 
350
    return ret;
 
351
}
 
352
 
240
353
bool KoDocument::saveFile()
241
354
{
242
 
  kdDebug(30003) << "KoDocument::saveFile()" << endl;
243
 
  if ( !kapp->inherits( "KoApplication" ) )
244
 
    return false;
245
 
 
246
 
  QCString _native_format = nativeFormatMimeType();
247
 
  // The output format is set by koMainWindow, and by openFile
248
 
  QCString outputMimeType = d->outputMimeType;
249
 
  ASSERT( !outputMimeType.isEmpty() );
250
 
  if ( outputMimeType.isEmpty() )
251
 
      outputMimeType = _native_format;
252
 
 
253
 
  QApplication::setOverrideCursor( waitCursor );
254
 
 
255
 
  if ( KIO::NetAccess::exists( m_url ) ) { // this file exists => backup
256
 
        // TODO : make this configurable ?
257
 
        KURL backup( m_url );
258
 
        backup.setPath( backup.path() + QString::fromLatin1("~") );
259
 
        (void) KIO::NetAccess::del( backup );
260
 
        (void) KIO::NetAccess::copy( m_url, backup );
261
 
 
262
 
        // This is noticeably faster, but not network transparent, and more importantly
263
 
        // it fails with '(' and other special chars in the filename.
264
 
        //QString cmd = QString( "rm -rf %1~" ).arg( url.path() );
265
 
        //system( cmd.local8Bit() );
266
 
        //cmd = QString("cp %1 %2~").arg( url.path() ).arg( url.path() );
267
 
        //system( cmd.local8Bit() );
268
 
  }
269
 
  bool ret;
270
 
  if ( outputMimeType != _native_format ) {
271
 
    kdDebug(30003) << "Saving to format " << outputMimeType << " in " << m_file << endl;
272
 
    // Not native format : save using export filter
273
 
    d->m_changed=false;
274
 
    if ( !d->filterManager )
275
 
        d->filterManager = new KoFilterManager();
276
 
    QString nativeFile = d->filterManager->prepareExport( m_file, _native_format, outputMimeType, this);
277
 
    kdDebug(30003) << "Temp native file " << nativeFile << endl;
278
 
 
279
 
    if(d->m_changed==false && nativeFile!=m_file) {
280
 
        ret = saveNativeFormat( nativeFile );
281
 
        if ( !ret )
282
 
            kdError(30003) << "Couldn't save in native format!" << endl;
283
 
        else
284
 
            ret = d->filterManager->export_();
 
355
    kdDebug(30003) << "KoDocument::saveFile() doc='" << url().url() <<"'"<< endl;
 
356
    if ( !kapp->inherits( "KoApplication" ) )
 
357
    {
 
358
        d->lastErrorMessage = i18n( "Internal error: not a KOffice application, saving not allowed." );
 
359
        return false;
 
360
    }
 
361
 
 
362
    QCString _native_format = nativeFormatMimeType();
 
363
    // The output format is set by koMainWindow, and by openFile
 
364
    QCString outputMimeType = d->outputMimeType;
 
365
    Q_ASSERT( !outputMimeType.isEmpty() );
 
366
    if ( outputMimeType.isEmpty() )
 
367
        outputMimeType = _native_format;
 
368
 
 
369
    QApplication::setOverrideCursor( waitCursor );
 
370
 
 
371
    if ( backupFile() ) {
 
372
        KIO::UDSEntry entry;
 
373
#if KDE_IS_VERSION(3,1,90)
 
374
        if ( KIO::NetAccess::stat( url(), entry, shells().current() ) ) { // this file exists => backup
 
375
#else
 
376
        if ( KIO::NetAccess::stat( url(), entry ) ) { // this file exists => backup
 
377
#endif
 
378
            emit sigStatusBarMessage( i18n("Making backup...") );
 
379
            KURL backup;
 
380
            if ( d->m_backupPath.isEmpty())
 
381
                backup = url();
 
382
            else
 
383
                backup = d->m_backupPath +"/"+url().fileName();
 
384
            backup.setPath( backup.path() + QString::fromLatin1("~") );
 
385
#if KDE_IS_VERSION(3,1,90)
 
386
            KIO::NetAccess::file_copy( url(), backup, -1, true /*overwrite*/, false /*resume*/, shells().current() );
 
387
#else
 
388
            KFileItem item( entry, url() );
 
389
            Q_ASSERT( item.name() == url().fileName() );
 
390
            KIO::NetAccess::del( backup ); // Copy does not remove existing destination file
 
391
            KIO::NetAccess::copy( url(), backup );
 
392
            // Not network transparent.
 
393
            if ( backup.isLocalFile() )
 
394
                ::chmod( QFile::encodeName( backup.path() ), item.permissions() );
 
395
#endif
 
396
        }
 
397
    }
 
398
 
 
399
    emit sigStatusBarMessage( i18n("Saving...") );
 
400
    bool ret = false;
 
401
    bool suppressErrorDialog = false;
 
402
    if ( outputMimeType != _native_format ) {
 
403
        kdDebug(30003) << "Saving to format " << outputMimeType << " in " << m_file << endl;
 
404
        // Not native format : save using export filter
 
405
        if ( !d->filterManager )
 
406
            d->filterManager = new KoFilterManager( this );
 
407
 
 
408
        KoFilter::ConversionStatus status = d->filterManager->exp0rt( m_file, outputMimeType );
 
409
        ret = status == KoFilter::OK;
 
410
        suppressErrorDialog = (status == KoFilter::UserCancelled || status == KoFilter::BadConversionGraph );
285
411
    } else {
286
 
      // How can this happen ? m_changed = true ?
287
 
      // No -> nativeFile==m_file :) (Werner)
288
 
      ret = true;
289
 
    }
290
 
  } else {
291
 
    // Native format => normal save
292
 
    ret = saveNativeFormat( m_file );
293
 
  }
294
 
 
295
 
  if ( ret )
296
 
  {
297
 
    // Eliminate any auto-save file
298
 
    QString asf = autoSaveFile( m_file );
299
 
    if ( QFile::exists( asf ) )
300
 
      unlink( QFile::encodeName( asf ) );
301
 
  }
302
 
 
303
 
  QApplication::restoreOverrideCursor();
304
 
  if ( !ret )
305
 
  {
306
 
    KMessageBox::error( 0L, i18n( "Could not save\n%1" ).arg( m_file ) );
307
 
  }
308
 
  return ret;
309
 
}
310
 
 
311
 
void KoDocument::setOutputMimeType( const QCString & mimeType )
 
412
        // Native format => normal save
 
413
        ret = saveNativeFormat( m_file );
 
414
    }
 
415
 
 
416
    if ( ret ) {
 
417
        removeAutoSaveFiles();
 
418
        // Restart the autosave timer
 
419
        // (we don't want to autosave again 2 seconds after a real save)
 
420
        setAutoSave( d->m_autoSaveDelay );
 
421
    }
 
422
 
 
423
    QApplication::restoreOverrideCursor();
 
424
    if ( !ret )
 
425
    {
 
426
        if ( !suppressErrorDialog )
 
427
        {
 
428
            if ( d->lastErrorMessage.isEmpty() )
 
429
                KMessageBox::error( 0L, i18n( "Could not save\n%1" ).arg( m_file ) );
 
430
            else if ( d->lastErrorMessage != "USER_CANCELED" )
 
431
            {
 
432
                KMessageBox::error( 0L, i18n( "Could not save %1\nReason: %2" ).arg( m_file, d->lastErrorMessage ) );
 
433
            }
 
434
        }
 
435
 
 
436
        // couldn't save file so this new URL is invalid
 
437
        // FIXME: we should restore the current document's true URL instead of
 
438
        // setting it to nothing otherwise anything that depends on the URL
 
439
        // being correct will not work (i.e. the document will be called
 
440
        // "Untitled" which may not be true)
 
441
        //
 
442
        // Update: now the URL is restored in KoMainWindow but really, this
 
443
        // should still be fixed in KoDocument/KParts (ditto for m_file).
 
444
        // We still resetURL() here since we may or may not have been called
 
445
        // by KoMainWindow - Clarence
 
446
        resetURL();
 
447
    }
 
448
 
 
449
    if ( ret )
 
450
    {
 
451
        d->mimeType = outputMimeType;
 
452
        setConfirmNonNativeSave ( isExporting (), false );
 
453
    }
 
454
    emit sigClearStatusBarMessage();
 
455
 
 
456
    return ret;
 
457
}
 
458
 
 
459
QCString KoDocument::mimeType() const
 
460
{
 
461
    return d->mimeType;
 
462
}
 
463
 
 
464
void KoDocument::setMimeType( const QCString & mimeType )
 
465
{
 
466
    d->mimeType = mimeType;
 
467
}
 
468
 
 
469
void KoDocument::setOutputMimeType( const QCString & mimeType, int specialOutputFlag )
312
470
{
313
471
    d->outputMimeType = mimeType;
 
472
    d->m_specialOutputFlag = specialOutputFlag;
314
473
}
315
474
 
316
475
QCString KoDocument::outputMimeType() const
318
477
    return d->outputMimeType;
319
478
}
320
479
 
321
 
void KoDocument::setFilterManager( KoFilterManager * manager )
322
 
{
323
 
    delete d->filterManager;
324
 
    d->filterManager = manager;
325
 
}
326
 
 
 
480
int KoDocument::specialOutputFlag() const
 
481
{
 
482
    return d->m_specialOutputFlag;
 
483
}
 
484
 
 
485
bool KoDocument::confirmNonNativeSave( const bool exporting ) const
 
486
{
 
487
    // "exporting ? 1 : 0" is different from "exporting" because a bool is
 
488
    // usually implemented like an "int", not "unsigned : 1"
 
489
    return d->m_confirmNonNativeSave [ exporting ? 1 : 0 ];
 
490
}
 
491
 
 
492
void KoDocument::setConfirmNonNativeSave( const bool exporting, const bool on )
 
493
{
 
494
    d->m_confirmNonNativeSave [ exporting ? 1 : 0] = on;
 
495
}
 
496
 
 
497
bool KoDocument::isImporting() const
 
498
{
 
499
    return d->m_isImporting;
 
500
}
 
501
 
 
502
bool KoDocument::isExporting() const
 
503
{
 
504
    return d->m_isExporting;
 
505
}
 
506
 
 
507
void KoDocument::setCheckAutoSaveFile( bool b )
 
508
{
 
509
    d->m_shouldCheckAutoSaveFile = b;
 
510
}
 
511
 
 
512
void KoDocument::setAutoErrorHandlingEnabled( bool b )
 
513
{
 
514
    d->m_autoErrorHandlingEnabled = b;
 
515
}
 
516
 
 
517
bool KoDocument::isAutoErrorHandlingEnabled()
 
518
{
 
519
    return d->m_autoErrorHandlingEnabled;
 
520
}
327
521
 
328
522
void KoDocument::slotAutoSave()
329
523
{
330
 
    //kdDebug() << "KoDocument::slotAutoSave m_file=" << m_file << endl;
331
 
    //kdDebug()<<"Autosave : modifiedAfterAutosave "<<d->modifiedAfterAutosave<<endl;
332
 
    if ( !m_file.isEmpty() && isModified() && d->modifiedAfterAutosave )
 
524
    //kdDebug(30003)<<"Autosave : modifiedAfterAutosave "<<d->modifiedAfterAutosave<<endl;
 
525
    if ( isModified() && d->modifiedAfterAutosave )
333
526
    {
334
 
        // TODO temporary message in statusbar ?
335
 
        /*bool ret =*/ saveNativeFormat( autoSaveFile( m_file ) );
 
527
        connect( this, SIGNAL( sigProgress( int ) ), shells().current(), SLOT( slotProgress( int ) ) );
 
528
        emit sigStatusBarMessage( i18n("Autosaving...") );
 
529
        d->m_autosaving = true;
 
530
        bool ret = saveNativeFormat( autoSaveFile( m_file ) );
336
531
        setModified( true );
337
 
        d->modifiedAfterAutosave=false;
 
532
        if ( ret )
 
533
            d->modifiedAfterAutosave=false;
 
534
        d->m_autosaving = false;
 
535
        emit sigClearStatusBarMessage();
 
536
        disconnect( this, SIGNAL( sigProgress( int ) ), shells().current(), SLOT( slotProgress( int ) ) );
 
537
        // Not enabled due to i18n freeze.
 
538
        //if ( !ret )
 
539
        //    emit sigStatusBarMessage( i18n("Error during autosave! Partition full?") );
338
540
    }
339
541
}
340
542
 
341
543
KAction *KoDocument::action( const QDomElement &element ) const
342
544
{
343
 
  return d->m_views.getFirst()->action( element );
 
545
    // First look in the document itself
 
546
    KAction* act = KParts::ReadWritePart::action( element );
 
547
    if ( act )
 
548
        return act;
 
549
 
 
550
    Q_ASSERT( d->m_bSingleViewMode );
 
551
    // Then look in the first view (this is for the single view mode)
 
552
    if ( !d->m_views.isEmpty() )
 
553
        return d->m_views.getFirst()->action( element );
 
554
    else
 
555
        return 0L;
344
556
}
345
557
 
346
558
QDomDocument KoDocument::domDocument() const
347
559
{
348
 
//  assert(!d->m_views.isEmpty());
349
 
//  return d->m_views.getFirst()->domDocument();
350
 
    return QDomDocument();
 
560
    // When embedded into e.g. konqueror, we want the view's GUI (hopefully a reduced one)
 
561
    // to be used.
 
562
    Q_ASSERT( d->m_bSingleViewMode );
 
563
    if ( d->m_views.isEmpty() )
 
564
        return QDomDocument();
 
565
    else
 
566
        return d->m_views.getFirst()->domDocument();
351
567
}
352
568
 
353
569
void KoDocument::setManager( KParts::PartManager *manager )
354
570
{
355
 
  KParts::ReadWritePart::setManager( manager );
356
 
  if ( d->m_bSingleViewMode && d->m_views.count() == 1 )
357
 
    d->m_views.getFirst()->setPartManager( manager );
358
 
 
359
 
  QListIterator<KoDocumentChild> it( d->m_children );
360
 
  for (; it.current(); ++it )
361
 
      if ( it.current()->document() )
362
 
          manager->addPart( it.current()->document(), false );
363
 
 
 
571
    KParts::ReadWritePart::setManager( manager );
 
572
    if ( d->m_bSingleViewMode && d->m_views.count() == 1 )
 
573
        d->m_views.getFirst()->setPartManager( manager );
 
574
 
 
575
    if ( manager )
 
576
    {
 
577
        QPtrListIterator<KoDocumentChild> it( d->m_children );
 
578
        for (; it.current(); ++it )
 
579
            if ( it.current()->document() )
 
580
                manager->addPart( it.current()->document(), false );
 
581
    }
364
582
}
365
583
 
366
584
void KoDocument::setReadWrite( bool readwrite )
367
585
{
368
 
  KParts::ReadWritePart::setReadWrite( readwrite );
369
 
 
370
 
  QListIterator<KoView> vIt( d->m_views );
371
 
  for (; vIt.current(); ++vIt )
372
 
    vIt.current()->updateReadWrite( readwrite );
373
 
 
374
 
  QListIterator<KoDocumentChild> dIt( d->m_children );
375
 
  for (; dIt.current(); ++dIt )
376
 
    if ( dIt.current()->document() )
377
 
      dIt.current()->document()->setReadWrite( readwrite );
378
 
 
379
 
  setAutoSave( d->m_autoSaveDelay );
 
586
    KParts::ReadWritePart::setReadWrite( readwrite );
 
587
 
 
588
    QPtrListIterator<KoView> vIt( d->m_views );
 
589
    for (; vIt.current(); ++vIt )
 
590
        vIt.current()->updateReadWrite( readwrite );
 
591
 
 
592
    QPtrListIterator<KoDocumentChild> dIt( d->m_children );
 
593
    for (; dIt.current(); ++dIt )
 
594
        if ( dIt.current()->document() )
 
595
            dIt.current()->document()->setReadWrite( readwrite );
 
596
 
 
597
    setAutoSave( d->m_autoSaveDelay );
380
598
}
381
599
 
382
600
void KoDocument::setAutoSave( int delay )
383
601
{
384
 
  d->m_autoSaveDelay = delay;
385
 
  if ( isReadWrite() && d->m_autoSaveDelay > 0 )
386
 
    d->m_autoSaveTimer.start( d->m_autoSaveDelay * 1000 );
387
 
  else
388
 
    d->m_autoSaveTimer.stop();
 
602
    d->m_autoSaveDelay = delay;
 
603
    if ( isReadWrite() && !isEmbedded() && d->m_autoSaveDelay > 0 )
 
604
        d->m_autoSaveTimer.start( d->m_autoSaveDelay * 1000 );
 
605
    else
 
606
        d->m_autoSaveTimer.stop();
389
607
}
390
608
 
391
609
void KoDocument::addView( KoView *view )
392
610
{
393
 
  if ( !view )
394
 
    return;
 
611
    if ( !view )
 
612
        return;
395
613
 
396
 
  d->m_views.append( view );
397
 
  view->updateReadWrite( isReadWrite() );
 
614
    d->m_views.append( view );
 
615
    view->updateReadWrite( isReadWrite() );
398
616
}
399
617
 
400
618
void KoDocument::removeView( KoView *view )
402
620
    d->m_views.removeRef( view );
403
621
}
404
622
 
405
 
const QList<KoView>& KoDocument::views() const
 
623
const QPtrList<KoView>& KoDocument::views() const
406
624
{
407
625
    return d->m_views;
408
626
}
409
627
 
410
628
int KoDocument::viewCount() const
411
629
{
412
 
  return d->m_views.count();
 
630
    return d->m_views.count();
413
631
}
414
632
 
415
633
void KoDocument::insertChild( KoDocumentChild *child )
416
634
{
417
 
  setModified( true );
418
 
 
419
 
  d->m_children.append( child );
420
 
 
421
 
  connect( child, SIGNAL( changed( KoChild * ) ),
422
 
           this, SLOT( slotChildChanged( KoChild * ) ) );
423
 
  connect( child, SIGNAL( destroyed() ),
424
 
           this, SLOT( slotChildDestroyed() ) );
425
 
 
426
 
  // It may be that insertChild is called without the KoDocumentChild
427
 
  // having a KoDocument attached, yet. This happens for example
428
 
  // when KPresenter loads a document with embedded objects. For those
429
 
  // KPresenterChild objects are allocated and insertChild is called.
430
 
  // Later in loadChildren() KPresenter iterates over the child list
431
 
  // and calls loadDocument for each child. That's exactly where we
432
 
  // will try to do what we cannot do now: Register the child document
433
 
  // at the partmanager (Simon)
434
 
  if ( manager() && !isSingleViewMode() && child->document() )
435
 
    manager()->addPart( child->document(), false );
 
635
    setModified( true );
 
636
 
 
637
    d->m_children.append( child );
 
638
 
 
639
    connect( child, SIGNAL( changed( KoChild * ) ),
 
640
             this, SLOT( slotChildChanged( KoChild * ) ) );
 
641
    connect( child, SIGNAL( destroyed() ),
 
642
             this, SLOT( slotChildDestroyed() ) );
 
643
 
 
644
    // It may be that insertChild is called without the KoDocumentChild
 
645
    // having a KoDocument attached, yet. This happens for example
 
646
    // when KPresenter loads a document with embedded objects. For those
 
647
    // KPresenterChild objects are allocated and insertChild is called.
 
648
    // Later in loadChildren() KPresenter iterates over the child list
 
649
    // and calls loadDocument for each child. That's exactly where we
 
650
    // will try to do what we cannot do now: Register the child document
 
651
    // at the partmanager (Simon)
 
652
    if ( manager() && !isSingleViewMode() && child->document() )
 
653
        manager()->addPart( child->document(), false );
436
654
}
437
655
 
438
656
void KoDocument::slotChildChanged( KoChild *c )
439
657
{
440
 
  assert( c->inherits( "KoDocumentChild" ) );
441
 
  emit childChanged( static_cast<KoDocumentChild *>( c ) );
 
658
    assert( c->inherits( "KoDocumentChild" ) );
 
659
    emit childChanged( static_cast<KoDocumentChild *>( c ) );
442
660
}
443
661
 
444
662
void KoDocument::slotChildDestroyed()
449
667
    d->m_children.removeRef( child );
450
668
}
451
669
 
452
 
const QList<KoDocumentChild>& KoDocument::children() const
 
670
const QPtrList<KoDocumentChild>& KoDocument::children() const
453
671
{
454
 
  return d->m_children;
 
672
    return d->m_children;
455
673
}
456
674
 
457
675
KParts::Part *KoDocument::hitTest( QWidget *widget, const QPoint &globalPos )
458
676
{
459
 
  QListIterator<KoView> it( d->m_views );
460
 
  for (; it.current(); ++it )
461
 
    if ( (QWidget *)it.current() == widget )
462
 
    {
463
 
      QPoint canvasPos( it.current()->canvas()->mapFromGlobal( globalPos ) );
464
 
      canvasPos.rx() += it.current()->canvasXOffset();
465
 
      canvasPos.ry() += it.current()->canvasYOffset();
466
 
 
467
 
      KParts::Part *part = it.current()->hitTest( canvasPos );
468
 
      if ( part )
469
 
        return part;
470
 
    }
471
 
 
472
 
  return 0L;
 
677
    QPtrListIterator<KoView> it( d->m_views );
 
678
    for (; it.current(); ++it )
 
679
        if ( (QWidget *)it.current() == widget )
 
680
        {
 
681
            QPoint canvasPos( it.current()->canvas()->mapFromGlobal( globalPos ) );
 
682
            canvasPos.rx() += it.current()->canvasXOffset();
 
683
            canvasPos.ry() += it.current()->canvasYOffset();
 
684
 
 
685
            KParts::Part *part = it.current()->hitTest( canvasPos );
 
686
            if ( part )
 
687
                return part;
 
688
        }
 
689
 
 
690
    return 0L;
473
691
}
474
692
 
475
693
KoDocument *KoDocument::hitTest( const QPoint &pos, const QWMatrix &matrix )
476
694
{
477
 
  QListIterator<KoDocumentChild> it( d->m_children );
478
 
  for (; it.current(); ++it )
479
 
  {
480
 
    KoDocument *doc = it.current()->hitTest( pos, matrix );
481
 
    if ( doc )
482
 
      return doc;
483
 
  }
 
695
    QPtrListIterator<KoDocumentChild> it( d->m_children );
 
696
    for (; it.current(); ++it )
 
697
    {
 
698
        KoDocument *doc = it.current()->hitTest( pos, matrix );
 
699
        if ( doc )
 
700
            return doc;
 
701
    }
484
702
 
485
 
  return this;
 
703
    return this;
486
704
}
487
705
 
488
706
KoDocumentChild *KoDocument::child( KoDocument *doc )
489
707
{
490
 
  QListIterator<KoDocumentChild> it( d->m_children );
491
 
  for (; it.current(); ++it )
492
 
    if ( it.current()->document() == doc )
493
 
      return it.current();
 
708
    QPtrListIterator<KoDocumentChild> it( d->m_children );
 
709
    for (; it.current(); ++it )
 
710
        if ( it.current()->document() == doc )
 
711
            return it.current();
494
712
 
495
 
  return 0L;
 
713
    return 0L;
496
714
}
497
715
 
498
716
KoDocumentInfo *KoDocument::documentInfo() const
499
717
{
500
 
  return d->m_docInfo;
 
718
    return d->m_docInfo;
501
719
}
502
720
 
503
721
void KoDocument::setViewBuildDocument( KoView *view, const QDomDocument &doc )
504
722
{
505
 
  if ( d->m_views.find( view ) == -1 )
506
 
    return;
507
 
 
508
 
  uint viewIdx = d->m_views.at();
509
 
 
510
 
  if ( d->m_viewBuildDocuments.count() == viewIdx )
511
 
  {
512
 
    d->m_viewBuildDocuments.append( doc );
513
 
  }
514
 
  else if ( d->m_viewBuildDocuments.count() > viewIdx )
515
 
  {
516
 
    d->m_viewBuildDocuments[ viewIdx ] = doc;
517
 
  }
 
723
    if ( d->m_views.find( view ) == -1 )
 
724
        return;
 
725
 
 
726
    uint viewIdx = d->m_views.at();
 
727
 
 
728
    if ( d->m_viewBuildDocuments.count() == viewIdx )
 
729
        d->m_viewBuildDocuments.append( doc );
 
730
    else if ( d->m_viewBuildDocuments.count() > viewIdx )
 
731
        d->m_viewBuildDocuments[ viewIdx ] = doc;
518
732
}
519
733
 
520
734
QDomDocument KoDocument::viewBuildDocument( KoView *view )
521
735
{
522
 
  QDomDocument res;
523
 
 
524
 
  if ( d->m_views.find( view ) == -1 )
525
 
    return res;
526
 
 
527
 
  uint viewIdx = d->m_views.at();
528
 
 
529
 
  if ( viewIdx >= d->m_viewBuildDocuments.count() )
530
 
    return res;
531
 
 
532
 
  res = d->m_viewBuildDocuments[ viewIdx ];
533
 
 
534
 
  // make this entry empty. otherwise we get a segfault in QMap ;-(
535
 
  d->m_viewBuildDocuments[ viewIdx ] = QDomDocument();
536
 
 
537
 
  return res;
 
736
    QDomDocument res;
 
737
 
 
738
    if ( d->m_views.find( view ) == -1 )
 
739
        return res;
 
740
 
 
741
    uint viewIdx = d->m_views.at();
 
742
 
 
743
    if ( viewIdx >= d->m_viewBuildDocuments.count() )
 
744
        return res;
 
745
 
 
746
    res = d->m_viewBuildDocuments[ viewIdx ];
 
747
 
 
748
    // make this entry empty. otherwise we get a segfault in QMap ;-(
 
749
    d->m_viewBuildDocuments[ viewIdx ] = QDomDocument();
 
750
 
 
751
    return res;
538
752
}
539
753
 
540
754
void KoDocument::paintEverything( QPainter &painter, const QRect &rect, bool transparent, KoView *view, double zoomX, double zoomY )
541
755
{
542
 
  paintContent( painter, rect, transparent, zoomX, zoomY );
543
 
  paintChildren( painter, rect, view, zoomX, zoomY );
 
756
    paintContent( painter, rect, transparent, zoomX, zoomY );
 
757
    paintChildren( painter, rect, view, zoomX, zoomY );
544
758
}
545
759
 
546
760
void KoDocument::paintChildren( QPainter &painter, const QRect &/*rect*/, KoView *view, double zoomX, double zoomY )
547
761
{
548
 
  QListIterator<KoDocumentChild> it( d->m_children );
549
 
  for (; it.current(); ++it )
550
 
  {
551
 
    // #### todo: paint only if child is visible inside rect
552
 
    painter.save();
553
 
    paintChild( it.current(), painter, view, zoomX, zoomY );
554
 
    painter.restore();
555
 
  }
 
762
    QPtrListIterator<KoDocumentChild> it( d->m_children );
 
763
    for (; it.current(); ++it )
 
764
    {
 
765
        // #### todo: paint only if child is visible inside rect
 
766
        painter.save();
 
767
        paintChild( it.current(), painter, view, zoomX, zoomY );
 
768
        painter.restore();
 
769
    }
556
770
}
557
771
 
558
772
void KoDocument::paintChild( KoDocumentChild *child, QPainter &painter, KoView *view, double zoomX, double zoomY )
559
773
{
 
774
    if ( child->isDeleted() )
 
775
        return;
 
776
 
560
777
    // QRegion rgn = painter.clipRegion();
561
778
 
562
 
  child->transform( painter );
563
 
  child->document()->paintEverything( painter, child->contentRect(), child->isTransparent(), view, zoomX, zoomY );
564
 
 
565
 
  if ( view && view->partManager() )
566
 
  {
567
 
      // ### do we need to apply zoomX and zoomY here ?
568
 
    KParts::PartManager *manager = view->partManager();
569
 
 
570
 
    painter.scale( 1.0 / child->xScaling(), 1.0 / child->yScaling() );
571
 
 
572
 
    int w = int( (double)child->contentRect().width() * child->xScaling() );
573
 
    int h = int( (double)child->contentRect().height() * child->yScaling() );
574
 
    if ( ( manager->selectedPart() == (KParts::Part *)child->document() &&
575
 
           manager->selectedWidget() == (QWidget *)view ) ||
576
 
         ( manager->activePart() == (KParts::Part *)child->document() &&
577
 
           manager->activeWidget() == (QWidget *)view ) )
 
779
    child->transform( painter );
 
780
    child->document()->paintEverything( painter, child->contentRect(), child->isTransparent(), view, zoomX, zoomY );
 
781
 
 
782
    if ( view && view->partManager() )
 
783
    {
 
784
        // ### do we need to apply zoomX and zoomY here ?
 
785
        KParts::PartManager *manager = view->partManager();
 
786
 
 
787
        painter.scale( 1.0 / child->xScaling(), 1.0 / child->yScaling() );
 
788
 
 
789
        int w = int( (double)child->contentRect().width() * child->xScaling() );
 
790
        int h = int( (double)child->contentRect().height() * child->yScaling() );
 
791
        if ( ( manager->selectedPart() == (KParts::Part *)child->document() &&
 
792
               manager->selectedWidget() == (QWidget *)view ) ||
 
793
             ( manager->activePart() == (KParts::Part *)child->document() &&
 
794
               manager->activeWidget() == (QWidget *)view ) )
578
795
        {
579
796
            // painter.setClipRegion( rgn );
580
 
          painter.setClipping( FALSE );
581
 
 
582
 
          painter.setPen( black );
583
 
          painter.fillRect( -5, -5, w + 10, 5, white );
584
 
          painter.fillRect( -5, h, w + 10, 5, white );
585
 
          painter.fillRect( -5, -5, 5, h + 10, white );
586
 
          painter.fillRect( w, -5, 5, h + 10, white );
587
 
          painter.fillRect( -5, -5, w + 10, 5, BDiagPattern );
588
 
          painter.fillRect( -5, h, w + 10, 5, BDiagPattern );
589
 
          painter.fillRect( -5, -5, 5, h + 10, BDiagPattern );
590
 
          painter.fillRect( w, -5, 5, h + 10, BDiagPattern );
591
 
 
592
 
          if ( manager->selectedPart() == (KParts::Part *)child->document() &&
593
 
               manager->selectedWidget() == (QWidget *)view )
594
 
          {
595
 
            QColor color;
596
 
            if ( view->koDocument() == this )
597
 
              color = black;
598
 
            else
599
 
              color = gray;
600
 
            painter.fillRect( -5, -5, 5, 5, color );
601
 
            painter.fillRect( -5, h, 5, 5, color );
602
 
            painter.fillRect( w, h, 5, 5, color );
603
 
            painter.fillRect( w, -5, 5, 5, color );
604
 
            painter.fillRect( w / 2 - 3, -5, 5, 5, color );
605
 
            painter.fillRect( w / 2 - 3, h, 5, 5, color );
606
 
            painter.fillRect( -5, h / 2 - 3, 5, 5, color );
607
 
            painter.fillRect( w, h / 2 - 3, 5, 5, color );
608
 
          }
609
 
 
610
 
          painter.setClipping( TRUE );
611
 
      }
612
 
  }
613
 
}
614
 
 
615
 
bool KoDocument::saveChildren( KoStore* /*_store*/, const QString& /*_path*/ )
616
 
{
617
 
  // Lets assume that we do not have children
618
 
  kdWarning(30003) << "KoDocument::saveChildren( KoStore*, const QString & )" << endl;
619
 
  kdWarning(30003) << "Not implemented ( not really an error )" << endl;
620
 
  return true;
621
 
}
622
 
 
623
 
bool KoDocument::saveNativeFormat( const QString & file )
624
 
{
625
 
  kdDebug(30003) << "Saving to store" << endl;
626
 
 
627
 
  QCString appIdentification( "KOffice " ); // We are limited in the number of chars.
628
 
  appIdentification += nativeFormatMimeType();
629
 
  appIdentification += '\004'; // Two magic bytes to make the identification
630
 
  appIdentification += '\006'; // more reliable (DF)
631
 
  KoStore* store = new KoStore( file, KoStore::Write, appIdentification );
632
 
 
633
 
  // Save childen first since they might get a new url
634
 
  if ( store->bad() || !saveChildren( store, QString(STORE_PROTOCOL) + ':' ) )
635
 
  {
636
 
    kdDebug(30003) << "store->bad()=" << store->bad() << "   aborting saving !" << endl;
637
 
    delete store;
638
 
    return false;
639
 
  }
640
 
 
641
 
  kdDebug(30003) << "Saving root" << endl;
642
 
  if ( store->open( "root" ) )
643
 
  {
644
 
    KoStoreDevice dev( store );
645
 
    if ( !saveToStream( &dev ) )
646
 
    {
647
 
      delete store;
648
 
      return false;
649
 
    }
650
 
    store->close();
651
 
  }
652
 
  else
653
 
  {
654
 
    delete store;
655
 
    return false;
656
 
  }
657
 
  if ( store->open( "documentinfo.xml" ) )
658
 
  {
659
 
    QDomDocument doc = d->m_docInfo->save();
660
 
    KoStoreDevice dev( store );
661
 
 
662
 
    QCString s = doc.toCString(); // this is already Utf8!
663
 
 
664
 
    dev.writeBlock( s.data(), s.length() );
665
 
    store->close();
666
 
  }
667
 
 
668
 
  bool ret = completeSaving( store );
669
 
  kdDebug(30003) << "Saving done" << endl;
670
 
  delete store;
671
 
  return ret;
 
797
            painter.setClipping( FALSE );
 
798
 
 
799
            painter.setPen( black );
 
800
            painter.fillRect( -5, -5, w + 10, 5, white );
 
801
            painter.fillRect( -5, h, w + 10, 5, white );
 
802
            painter.fillRect( -5, -5, 5, h + 10, white );
 
803
            painter.fillRect( w, -5, 5, h + 10, white );
 
804
            painter.fillRect( -5, -5, w + 10, 5, BDiagPattern );
 
805
            painter.fillRect( -5, h, w + 10, 5, BDiagPattern );
 
806
            painter.fillRect( -5, -5, 5, h + 10, BDiagPattern );
 
807
            painter.fillRect( w, -5, 5, h + 10, BDiagPattern );
 
808
 
 
809
            if ( manager->selectedPart() == (KParts::Part *)child->document() &&
 
810
                 manager->selectedWidget() == (QWidget *)view )
 
811
            {
 
812
                QColor color;
 
813
                if ( view->koDocument() == this )
 
814
                    color = black;
 
815
                else
 
816
                    color = gray;
 
817
                painter.fillRect( -5, -5, 5, 5, color );
 
818
                painter.fillRect( -5, h, 5, 5, color );
 
819
                painter.fillRect( w, h, 5, 5, color );
 
820
                painter.fillRect( w, -5, 5, 5, color );
 
821
                painter.fillRect( w / 2 - 3, -5, 5, 5, color );
 
822
                painter.fillRect( w / 2 - 3, h, 5, 5, color );
 
823
                painter.fillRect( -5, h / 2 - 3, 5, 5, color );
 
824
                painter.fillRect( w, h / 2 - 3, 5, 5, color );
 
825
            }
 
826
 
 
827
            painter.setClipping( TRUE );
 
828
        }
 
829
    }
 
830
}
 
831
 
 
832
bool KoDocument::isModified()
 
833
{
 
834
    if ( KParts::ReadWritePart::isModified() )
 
835
    {
 
836
        //kdDebug(30003)<<k_funcinfo<<" Modified doc='"<<url().url()<<"' extern="<<isStoredExtern()<<endl;
 
837
        return true;
 
838
    }
 
839
    // Then go through internally stored children (considdered to be part of this doc)
 
840
    QPtrListIterator<KoDocumentChild> it = children();
 
841
    for (; it.current(); ++it )
 
842
    {
 
843
        if ( !it.current()->isStoredExtern() && !it.current()->isDeleted() )
 
844
        {
 
845
            KoDocument *doc = it.current()->document();
 
846
            if ( doc && doc->isModified() )
 
847
                return true;
 
848
        }
 
849
    }
 
850
    return false;
 
851
}
 
852
 
 
853
bool KoDocument::saveChildren( KoStore* _store )
 
854
{
 
855
    //kdDebug(30003)<<k_funcinfo<<" checking children of doc='"<<url().url()<<"'"<<endl;
 
856
    int i = 0;
 
857
    QPtrListIterator<KoDocumentChild> it( children() );
 
858
    for( ; it.current(); ++it ) {
 
859
        KoDocument* childDoc = it.current()->document();
 
860
        if (childDoc && !it.current()->isDeleted())
 
861
        {
 
862
            if ( !childDoc->isStoredExtern() )
 
863
            {
 
864
                //kdDebug(30003) << "KoDocument::saveChildren internal url: /" << i << endl;
 
865
                if ( !childDoc->saveToStore( _store, QString::number( i++ ) ) )
 
866
                    return FALSE;
 
867
 
 
868
                if (!isExporting ())
 
869
                    childDoc->setModified( false );
 
870
            }
 
871
            //else kdDebug(30003)<<k_funcinfo<<" external (don't save) url:" << childDoc->url().url()<<endl;
 
872
        }
 
873
    }
 
874
    return true;
 
875
}
 
876
 
 
877
bool KoDocument::saveExternalChildren()
 
878
{
 
879
    if ( d->m_doNotSaveExtDoc )
 
880
    {
 
881
        //kdDebug(30003)<<k_funcinfo<<" Don't save external docs in doc='"<<url().url()<<"'"<<endl;
 
882
        d->m_doNotSaveExtDoc = false;
 
883
        return true;
 
884
    }
 
885
 
 
886
    //kdDebug(30003)<<k_funcinfo<<" checking children of doc='"<<url().url()<<"'"<<endl;
 
887
    KoDocument *doc;
 
888
    KoDocumentChild *ch;
 
889
    QPtrListIterator<KoDocumentChild> it = children();
 
890
    for (; (ch = it.current()); ++it )
 
891
    {
 
892
        if ( !ch->isDeleted() )
 
893
        {
 
894
            doc = ch->document();
 
895
            if ( doc->isStoredExtern() && doc->isModified() )
 
896
            {
 
897
                kdDebug(30003)<<" save external doc='"<<url().url()<<"'"<<endl;
 
898
                doc->setDoNotSaveExtDoc(); // Only save doc + it's internal children
 
899
                if ( !doc->save() )
 
900
                    return false; // error
 
901
            }
 
902
            //kdDebug(30003)<<k_funcinfo<<" not modified doc='"<<url().url()<<"'"<<endl;
 
903
            // save possible external docs inside doc
 
904
            if ( !doc->saveExternalChildren() )
 
905
                return false;
 
906
        }
 
907
    }
 
908
    return true;
 
909
}
 
910
 
 
911
bool KoDocument::saveNativeFormat( const QString & _file )
 
912
{
 
913
    QString file( _file );
 
914
    d->lastErrorMessage = QString::null;
 
915
    //kdDebug(30003) << "Saving to store" << endl;
 
916
 
 
917
    KoStore::Backend backend = KoStore::Auto;
 
918
    if ( d->m_specialOutputFlag == SaveAsKOffice1dot1 )
 
919
    {
 
920
        kdDebug(30003) << "Saving as KOffice-1.1 format, using a tar.gz" << endl;
 
921
        backend = KoStore::Tar; // KOffice-1.0/1.1 used tar.gz for the native mimetype
 
922
        //// TODO more backwards compat stuff (embedded docs etc.)
 
923
    }
 
924
    else if ( d->m_specialOutputFlag == SaveAsDirectoryStore )
 
925
    {
 
926
        backend = KoStore::Directory;
 
927
        kdDebug(30003) << "Saving as uncompressed XML, using directory store." << endl;
 
928
    }
 
929
 
 
930
    kdDebug(30003) << "KoDocument::saveNativeFormat nativeFormatMimeType=" << nativeFormatMimeType() << endl;
 
931
    KoStore* store = KoStore::createStore( file, KoStore::Write, nativeFormatMimeType(), backend );
 
932
    if ( store->bad() )
 
933
    {
 
934
        d->lastErrorMessage = i18n( "Couldn't open the file for saving" ); // more details needed?
 
935
        delete store;
 
936
        return false;
 
937
    }
 
938
 
 
939
    // Save internal children first since they might get a new url
 
940
    if ( !saveChildren( store ) )
 
941
    {
 
942
        if ( d->lastErrorMessage.isEmpty() )
 
943
            d->lastErrorMessage = i18n( "Error while saving embedded documents" ); // more details needed
 
944
        delete store;
 
945
        return false;
 
946
    }
 
947
 
 
948
    kdDebug(30003) << "Saving root" << endl;
 
949
    if ( store->open( "root" ) )
 
950
    {
 
951
        KoStoreDevice dev( store );
 
952
        if ( !saveToStream( &dev ) )
 
953
        {
 
954
            kdDebug(30003) << "saveToStream failed" << endl;
 
955
            delete store;
 
956
            return false;
 
957
        }
 
958
        if ( !store->close() )
 
959
            return false;
 
960
    }
 
961
    else
 
962
    {
 
963
        d->lastErrorMessage = i18n( "Not able to write 'maindoc.xml'." );
 
964
        delete store;
 
965
        return false;
 
966
    }
 
967
    if ( store->open( "documentinfo.xml" ) )
 
968
    {
 
969
        QDomDocument doc = d->m_docInfo->save();
 
970
        KoStoreDevice dev( store );
 
971
 
 
972
        QCString s = doc.toCString(); // this is already Utf8!
 
973
        (void)dev.writeBlock( s.data(), s.size()-1 );
 
974
        (void)store->close();
 
975
    }
 
976
    if ( store->open( "preview.png" ) )
 
977
    {
 
978
        savePreview( store );
 
979
        (void)store->close();
 
980
    }
 
981
 
 
982
    bool ret = completeSaving( store );
 
983
    kdDebug(30003) << "Saving done of url: " << url().url() << endl;
 
984
    delete store;
 
985
 
 
986
    if ( !saveExternalChildren() )
 
987
    {
 
988
        return false;
 
989
    }
 
990
    return ret;
672
991
}
673
992
 
674
993
bool KoDocument::saveToStream( QIODevice * dev )
675
994
{
676
 
  QDomDocument doc = saveXML();
677
 
  // Save to buffer
678
 
  QCString s = doc.toCString(); // utf8 already!!!
679
 
  return dev->writeBlock( s.data(), s.length() ) == (int)s.length();
 
995
    QDomDocument doc = saveXML();
 
996
    // Save to buffer
 
997
    QCString s = doc.toCString(); // utf8 already
 
998
    // Important: don't use s.length() here. It's slow, and dangerous (in case of a '\0' somewhere)
 
999
    // The -1 is because we don't want to write the final \0.
 
1000
    int nwritten = dev->writeBlock( s.data(), s.size()-1 );
 
1001
    if ( nwritten != (int)s.size()-1 )
 
1002
        kdWarning(30003) << "KoDocument::saveToStream wrote " << nwritten << "   - expected " << s.size()-1 << endl;
 
1003
    return nwritten == (int)s.size()-1;
680
1004
}
681
1005
 
682
1006
bool KoDocument::saveToStore( KoStore* _store, const QString & _path )
683
1007
{
684
 
  kdDebug(30003) << "Saving document to store" << endl;
685
 
 
686
 
  // Use the path as the internal url
687
 
  m_url = _path;
688
 
 
689
 
  // Save childen first since they might get a new url
690
 
  if ( !saveChildren( _store, _path ) )
691
 
    return false;
692
 
 
693
 
  QString u = url().url();
694
 
  if ( _store->open( u ) )
695
 
  {
696
 
    KoStoreDevice dev( _store );
697
 
    if ( !saveToStream( &dev ) )
698
 
    {
699
 
      _store->close();
700
 
      return false;
701
 
    }
702
 
 
703
 
    _store->close();
704
 
  }
705
 
 
706
 
  if ( !completeSaving( _store ) )
707
 
    return false;
708
 
 
709
 
  kdDebug(30003) << "Saved document to store" << endl;
710
 
 
711
 
  return true;
 
1008
    kdDebug(30003) << "Saving document to store " << _path << endl;
 
1009
 
 
1010
    // Use the path as the internal url
 
1011
    if ( _path.startsWith( STORE_PROTOCOL ) )
 
1012
        m_url = KURL( _path );
 
1013
    else // ugly hack to pass a relative URI
 
1014
        m_url = KURL( INTERNAL_PREFIX +  _path );
 
1015
 
 
1016
    // To make the children happy cd to the correct directory
 
1017
    _store->pushDirectory();
 
1018
    _store->enterDirectory( _path );
 
1019
 
 
1020
    // Save childen first since they might get a new url
 
1021
    if ( !saveChildren( _store ) )
 
1022
        return false;
 
1023
 
 
1024
    // In the current directory we're the king :-)
 
1025
    if ( _store->open( "root" ) )
 
1026
    {
 
1027
        KoStoreDevice dev( _store );
 
1028
        if ( !saveToStream( &dev ) )
 
1029
        {
 
1030
            _store->close();
 
1031
            return false;
 
1032
        }
 
1033
        if ( !_store->close() )
 
1034
            return false;
 
1035
    }
 
1036
 
 
1037
    if ( !completeSaving( _store ) )
 
1038
        return false;
 
1039
 
 
1040
    // Now that we're done leave the directory again
 
1041
    _store->popDirectory();
 
1042
 
 
1043
    kdDebug(30003) << "Saved document to store" << endl;
 
1044
 
 
1045
    return true;
 
1046
}
 
1047
 
 
1048
void KoDocument::savePreview( KoStore* store )
 
1049
{
 
1050
    QPixmap pix = generatePreview(QSize(256, 256));
 
1051
    // Reducing to 8bpp reduces file sizes quite a lot.
 
1052
    QImageIO imageIO;
 
1053
    imageIO.setImage( pix.convertToImage().convertDepth(8, Qt::AvoidDither | Qt::DiffuseDither) );
 
1054
 
 
1055
    // NOTE: we cannot use QDataStream, as it is not 1:1
 
1056
    QByteArray imageData;
 
1057
    QBuffer buffer(imageData);
 
1058
    buffer.open(IO_WriteOnly);
 
1059
    imageIO.setIODevice(&buffer);
 
1060
    imageIO.setFormat("PNG");
 
1061
    imageIO.write();
 
1062
    buffer.close();
 
1063
 
 
1064
    store->write( imageData );
 
1065
}
 
1066
 
 
1067
QPixmap KoDocument::generatePreview( const QSize& size )
 
1068
{
 
1069
    double docWidth, docHeight;
 
1070
    int pixmapSize = QMAX(size.width(), size.height());
 
1071
 
 
1072
    if (m_pageLayout.ptWidth > 1.0) {
 
1073
        docWidth = m_pageLayout.ptWidth / 72 * QPaintDevice::x11AppDpiX();
 
1074
        docHeight = m_pageLayout.ptHeight / 72 * QPaintDevice::x11AppDpiX();
 
1075
 
 
1076
    } else {
 
1077
        // If we don't have a page layout, just draw the top left hand corner
 
1078
        docWidth = 500.0;
 
1079
        docHeight = 500.0;
 
1080
    }
 
1081
 
 
1082
    double ratio = docWidth / docHeight;
 
1083
 
 
1084
    QPixmap pix;
 
1085
    int previewWidth, previewHeight;
 
1086
    if (ratio > 1.0)
 
1087
    {
 
1088
        previewWidth = (int) pixmapSize;
 
1089
        previewHeight = (int) (pixmapSize / ratio);
 
1090
    }
 
1091
    else
 
1092
    {
 
1093
        previewWidth = (int) (pixmapSize * ratio);
 
1094
        previewHeight = (int) pixmapSize;
 
1095
    }
 
1096
 
 
1097
    pix.resize((int)docWidth, (int)docHeight);
 
1098
 
 
1099
    pix.fill( QColor( 245, 245, 245 ) );
 
1100
 
 
1101
    QRect rc(0, 0, pix.width(), pix.height());
 
1102
 
 
1103
    QPainter p;
 
1104
    p.begin(&pix);
 
1105
    paintEverything(p, rc, false);
 
1106
    p.end();
 
1107
 
 
1108
    pix.convertFromImage(pix.convertToImage().smoothScale(previewWidth, previewHeight));
 
1109
 
 
1110
    return pix;
712
1111
}
713
1112
 
714
1113
QString KoDocument::autoSaveFile( const QString & path ) const
715
1114
{
716
 
    KURL url( path );
717
 
    ASSERT( url.isLocalFile() );
718
 
    QString dir = url.directory(false);
719
 
    QString filename = url.filename();
720
 
    return dir + '.' + filename + ".autosave";
 
1115
    // Using the extension allows to avoid relying on the mime magic when opening
 
1116
    KMimeType::Ptr mime = KMimeType::mimeType( nativeFormatMimeType() );
 
1117
    QString extension = mime->property( "X-KDE-NativeExtension" ).toString();
 
1118
    if ( path.isEmpty() )
 
1119
    {
 
1120
        // Never saved? Use a temp file in $HOME then
 
1121
        // Yes, two open unnamed docs will overwrite each other's autosave file,
 
1122
        // but hmm, we can only do something if that's in the same process anyway...
 
1123
        QString ret = QDir::homeDirPath() + "/." + QString::fromLatin1(instance()->instanceName()) + ".autosave" + extension;
 
1124
        return ret;
 
1125
    }
 
1126
    else
 
1127
    {
 
1128
        KURL url( path );
 
1129
        Q_ASSERT( url.isLocalFile() );
 
1130
        QString dir = url.directory(false);
 
1131
        QString filename = url.fileName();
 
1132
        return dir + "." + filename + ".autosave" + extension;
 
1133
    }
 
1134
}
 
1135
 
 
1136
bool KoDocument::checkAutoSaveFile()
 
1137
{
 
1138
    QString asf = autoSaveFile( QString::null ); // the one in $HOME
 
1139
    //kdDebug(30003) << "asf=" << asf << endl;
 
1140
    if ( QFile::exists( asf ) )
 
1141
    {
 
1142
        QDateTime date = QFileInfo(asf).lastModified();
 
1143
        QString dateStr = date.toString(Qt::LocalDate);
 
1144
        int res = KMessageBox::warningYesNoCancel(
 
1145
            0, i18n( "An autosaved file for an unnamed document exists in %1.\nThis file is dated %2\nDo you want to open it?" )
 
1146
            .arg(asf, dateStr) );
 
1147
        switch(res) {
 
1148
        case KMessageBox::Yes : {
 
1149
            KURL url;
 
1150
            url.setPath( asf );
 
1151
            bool ret = openURL( url );
 
1152
            if ( ret )
 
1153
                resetURL();
 
1154
            return ret;
 
1155
        }
 
1156
        case KMessageBox::No :
 
1157
            unlink( QFile::encodeName( asf ) );
 
1158
            return false;
 
1159
        default: // Cancel
 
1160
            return false;
 
1161
        }
 
1162
    }
 
1163
    return false;
 
1164
}
 
1165
 
 
1166
bool KoDocument::import( const KURL & _url )
 
1167
{
 
1168
    bool ret;
 
1169
 
 
1170
    kdDebug (30003) << "KoDocument::import url=" << _url.url() << endl;
 
1171
    d->m_isImporting = true;
 
1172
 
 
1173
    // open...
 
1174
    ret = openURL (_url);
 
1175
 
 
1176
    // reset m_url & m_file (kindly? set by KParts::openURL()) to simulate a
 
1177
    // File --> Import
 
1178
    if (ret)
 
1179
    {
 
1180
        kdDebug (30003) << "KoDocument::import success, resetting url" << endl;
 
1181
        resetURL ();
 
1182
        setTitleModified ();
 
1183
    }
 
1184
 
 
1185
    d->m_isImporting = false;
 
1186
 
 
1187
    return ret;
721
1188
}
722
1189
 
723
1190
bool KoDocument::openURL( const KURL & _url )
724
1191
{
725
 
  // Reimplemented, to add a check for autosave files
726
 
  if ( _url.isMalformed() )
727
 
    return false;
728
 
  if ( !closeURL() )
729
 
    return false;
730
 
  KURL url( _url );
731
 
  bool autosaveOpened = false;
732
 
  if ( url.isLocalFile() )
733
 
  {
734
 
    QString file = url.path();
735
 
    QString asf = autoSaveFile( file );
736
 
    if ( QFile::exists( asf ) )
737
 
    {
738
 
      kdDebug() << "KoDocument::openURL asf=" << asf << endl;
739
 
      // ## TODO compare timestamps ?
740
 
      int res = KMessageBox::warningYesNoCancel( 0,
741
 
            i18n( "An autosaved file exists for this document.\nDo you want to open it instead ?" ));
742
 
      switch(res) {
743
 
      case KMessageBox::Yes :
744
 
        url.setPath( asf );
745
 
        autosaveOpened = true;
746
 
        break;
747
 
      case KMessageBox::No :
748
 
        unlink( QFile::encodeName( asf ) );
749
 
        break;
750
 
      default: // Cancel
751
 
        return false;
752
 
      }
753
 
    }
754
 
  }
755
 
  bool ret = false;
756
 
  if ( url.isLocalFile() )
757
 
  {
758
 
      // ReadOnlyPart::openURL does something wrong for local files: it emits completed
759
 
      // even if openFile returned false :(
760
 
      // So, fixing here:
761
 
      m_url = url;
762
 
      m_file = m_url.path();
763
 
      ret = openFile();
764
 
      if ( ret )
765
 
          emit completed();
766
 
  }
767
 
  else
768
 
  {
769
 
      ret = KParts::ReadWritePart::openURL( url );
770
 
  }
771
 
  if ( autosaveOpened )
772
 
      m_url = KURL(); // Force save to act like 'Save As'
773
 
  else
774
 
  {
775
 
      if ( d->m_shells.isEmpty() )
776
 
          kdWarning() << "KoDocument::openURL no shell yet !" << endl;
777
 
      // Add to recent actions list in our shells
778
 
      QListIterator<KoMainWindow> it( d->m_shells );
779
 
      for (; it.current(); ++it )
780
 
          it.current()->addRecentURL( _url );
781
 
  }
782
 
  return ret;
 
1192
    kdDebug(30003) << "KoDocument::openURL url=" << _url.url() << endl;
 
1193
    d->lastErrorMessage = QString::null;
 
1194
 
 
1195
    // Reimplemented, to add a check for autosave files and to improve error reporting
 
1196
    if ( !_url.isValid() )
 
1197
    {
 
1198
        d->lastErrorMessage = i18n( "Malformed URL\n%1" ).arg( _url.url() ); // ## used anywhere ?
 
1199
        return false;
 
1200
    }
 
1201
    if ( !closeURL() )
 
1202
        return false;
 
1203
    KURL url( _url );
 
1204
    bool autosaveOpened = false;
 
1205
    if ( url.isLocalFile() && d->m_shouldCheckAutoSaveFile )
 
1206
    {
 
1207
        QString file = url.path();
 
1208
        QString asf = autoSaveFile( file );
 
1209
        if ( QFile::exists( asf ) )
 
1210
        {
 
1211
            //kdDebug(30003) << "KoDocument::openURL asf=" << asf << endl;
 
1212
            // ## TODO compare timestamps ?
 
1213
            int res = KMessageBox::warningYesNoCancel( 0,
 
1214
                                                       i18n( "An autosaved file exists for this document.\nDo you want to open it instead?" ));
 
1215
            switch(res) {
 
1216
                case KMessageBox::Yes :
 
1217
                    url.setPath( asf );
 
1218
                    autosaveOpened = true;
 
1219
                    break;
 
1220
                case KMessageBox::No :
 
1221
                    unlink( QFile::encodeName( asf ) );
 
1222
                    break;
 
1223
                default: // Cancel
 
1224
                    return false;
 
1225
            }
 
1226
        }
 
1227
    }
 
1228
    bool ret = KParts::ReadWritePart::openURL( url );
 
1229
 
 
1230
    if ( autosaveOpened )
 
1231
        resetURL(); // Force save to act like 'Save As'
 
1232
    else
 
1233
    {
 
1234
        // We have no koffice shell when we are being embedded as a readonly part.
 
1235
        //if ( d->m_shells.isEmpty() )
 
1236
        //    kdWarning(30003) << "KoDocument::openURL no shell yet !" << endl;
 
1237
        // Add to recent actions list in our shells
 
1238
        QPtrListIterator<KoMainWindow> it( d->m_shells );
 
1239
        for (; it.current(); ++it )
 
1240
            it.current()->addRecentURL( _url );
 
1241
    }
 
1242
    return ret;
783
1243
}
784
1244
 
785
1245
bool KoDocument::openFile()
786
1246
{
787
 
  kdDebug(30003) << "KoDocument::openFile for " << m_file << endl;
788
 
  if ( ! QFile::exists(m_file) )
789
 
  {
790
 
    // Maybe offer to create a new document with that name ?
791
 
    KMessageBox::error(0L, i18n("The file %1 doesn't exist.").arg(m_file) );
792
 
    return false;
793
 
  }
794
 
 
795
 
  QApplication::setOverrideCursor( waitCursor );
796
 
 
797
 
  if ( d->m_bSingleViewMode && !d->m_views.isEmpty() )
798
 
  {
799
 
     // We already had a view (this happens when doing reload in konqueror)
800
 
     removeView( d->m_views.first() );
801
 
     delete d->m_views.first();
802
 
     ASSERT( d->m_views.isEmpty() );
803
 
  }
804
 
 
805
 
  d->m_changed=false;
806
 
  QCString _native_format = nativeFormatMimeType();
807
 
 
808
 
  // Launch a filter if we need one for this url ?
809
 
  if ( !d->filterManager )
810
 
      d->filterManager = new KoFilterManager();
811
 
  QString importedFile = d->filterManager->import( m_file, _native_format, this );
812
 
 
813
 
  kdDebug(30003) << "KoDocument::openFile - importedFile " << importedFile << endl;
814
 
 
815
 
  QApplication::restoreOverrideCursor();
816
 
 
817
 
  bool ok = true;
818
 
 
819
 
  if (!importedFile.isEmpty()) // Something to load (tmp or native file) ?
820
 
  {
821
 
    // The filter, if any, has been applied. It's all native format now.
822
 
    if ( !loadNativeFormat( importedFile ) )
823
 
    {
824
 
      ok = false;
825
 
      KMessageBox::error( 0L, i18n( "Could not open\n%1" ).arg( url().prettyURL() ) );
826
 
    }
827
 
  }
828
 
  else {
829
 
    // The filter did it all. Ok if it changed something...
830
 
    ok = d->m_changed;
831
 
  }
832
 
 
833
 
  if ( importedFile != m_file )
834
 
  {
835
 
    // We opened a temporary file (result of an import filter)
836
 
    // Set document URL to empty - we don't want to save in /tmp !
837
 
    // But only if in readwrite mode (no saving problem otherwise)
838
 
    if ( isReadWrite() )
839
 
      m_url = KURL();
840
 
    // and remove temp file - uncomment this to debug import filters
841
 
    if(!importedFile.isEmpty())
842
 
      unlink( QFile::encodeName(importedFile) );
843
 
  }
844
 
 
845
 
  if ( ok && d->m_bSingleViewMode )
846
 
  {
847
 
    KoView *view = createView( d->m_wrapperWidget );
848
 
    d->m_wrapperWidget->setKoView( view );
849
 
    view->show();
850
 
  }
851
 
 
852
 
  // We decided not to save in the file's original format by default
853
 
  // ( KWord isn't a text editor or a MSWord editor :)
854
 
  // The risk of losing formatting information is too high currently.
855
 
  d->outputMimeType = _native_format;
856
 
 
857
 
  return ok;
 
1247
    //kdDebug(30003) << "KoDocument::openFile for " << m_file << endl;
 
1248
    if ( ! QFile::exists(m_file) )
 
1249
    {
 
1250
        QApplication::restoreOverrideCursor();
 
1251
        if ( d->m_autoErrorHandlingEnabled )
 
1252
            // Maybe offer to create a new document with that name ?
 
1253
            KMessageBox::error(0L, i18n("The file %1 doesn't exist.").arg(m_file) );
 
1254
        return false;
 
1255
    }
 
1256
 
 
1257
    QApplication::setOverrideCursor( waitCursor );
 
1258
 
 
1259
    if ( d->m_bSingleViewMode && !d->m_views.isEmpty() )
 
1260
    {
 
1261
        // We already had a view (this happens when doing reload in konqueror)
 
1262
        KoView* v = d->m_views.first();
 
1263
        removeView( v );
 
1264
        delete v;
 
1265
        Q_ASSERT( d->m_views.isEmpty() );
 
1266
    }
 
1267
 
 
1268
    d->m_specialOutputFlag = 0;
 
1269
    QCString _native_format = nativeFormatMimeType();
 
1270
 
 
1271
    KURL u;
 
1272
    u.setPath( m_file );
 
1273
    QString typeName = KMimeType::findByURL( u, 0, true )->name();
 
1274
 
 
1275
    // Allow to open backup files, don't keep the mimetype application/x-trash.
 
1276
    if ( typeName == "application/x-trash" )
 
1277
    {
 
1278
        QString path = u.path();
 
1279
        QStringList patterns = KMimeType::mimeType( typeName )->patterns();
 
1280
        // Find the extension that makes it a backup file, and remove it
 
1281
        for( QStringList::Iterator it = patterns.begin(); it != patterns.end(); ++it ) {
 
1282
            QString ext = *it;
 
1283
            if ( !ext.isEmpty() && ext[0] == '*' )
 
1284
            {
 
1285
                ext.remove(0, 1);
 
1286
                if ( path.endsWith( ext ) ) {
 
1287
                    path.truncate( path.length() - ext.length() );
 
1288
                    break;
 
1289
                }
 
1290
            }
 
1291
        }
 
1292
        typeName = KMimeType::findByPath( path, 0, true )->name();
 
1293
    }
 
1294
 
 
1295
    // Special case for flat XML files (e.g. using directory store)
 
1296
    if ( u.fileName() == "maindoc.xml" || typeName == "inode/directory" )
 
1297
    {
 
1298
        typeName = _native_format; // Hmm, what if it's from another app? ### Check mimetype
 
1299
        d->m_specialOutputFlag = SaveAsDirectoryStore;
 
1300
        kdDebug(30003) << "KoDocument::openFile loading maindoc.xml, using directory store for " << m_file << endl;
 
1301
    }
 
1302
    kdDebug(30003) << "KoDocument::openFile " << m_file << " type:" << typeName << endl;
 
1303
 
 
1304
    QString importedFile = m_file;
 
1305
 
 
1306
    if ( typeName == KMimeType::defaultMimeType() ) {
 
1307
        kdError(30003) << "No mimetype found for " << m_file << endl;
 
1308
        QApplication::restoreOverrideCursor();
 
1309
        if ( d->m_autoErrorHandlingEnabled )
 
1310
            KMessageBox::error( 0L, i18n( "Could not open\n%1" ).arg( url().prettyURL( 0, KURL::StripFileProtocol ) ) );
 
1311
        return false;
 
1312
    }
 
1313
 
 
1314
    if ( typeName.latin1() != _native_format ) {
 
1315
        if ( !d->filterManager )
 
1316
            d->filterManager = new KoFilterManager( this );
 
1317
        KoFilter::ConversionStatus status;
 
1318
        importedFile = d->filterManager->import( m_file, status );
 
1319
        if ( status != KoFilter::OK )
 
1320
        {
 
1321
            QApplication::restoreOverrideCursor();
 
1322
            if ( status != KoFilter::UserCancelled &&
 
1323
                 status != KoFilter::BadConversionGraph &&
 
1324
                 d->m_autoErrorHandlingEnabled )
 
1325
                // Any way of passing a better error message from the filter?
 
1326
                KMessageBox::error( 0L, i18n( "Could not open\n%1" ).arg( url().prettyURL( 0, KURL::StripFileProtocol ) ) );
 
1327
 
 
1328
            return false;
 
1329
        }
 
1330
        kdDebug(30003) << "KoDocument::openFile - importedFile '" << importedFile
 
1331
                       << "', status: " << static_cast<int>( status ) << endl;
 
1332
    }
 
1333
 
 
1334
    QApplication::restoreOverrideCursor();
 
1335
 
 
1336
    bool ok = true;
 
1337
 
 
1338
    if (!importedFile.isEmpty()) // Something to load (tmp or native file) ?
 
1339
    {
 
1340
        // The filter, if any, has been applied. It's all native format now.
 
1341
        if ( !loadNativeFormat( importedFile ) )
 
1342
        {
 
1343
            ok = false;
 
1344
            if ( d->m_autoErrorHandlingEnabled )
 
1345
            {
 
1346
                if ( d->lastErrorMessage.isEmpty() )
 
1347
                    KMessageBox::error( 0L, i18n( "Could not open\n%1" ).arg( url().prettyURL( 0, KURL::StripFileProtocol ) ) );
 
1348
                else if ( d->lastErrorMessage != "USER_CANCELED" )
 
1349
                    KMessageBox::error( 0L, i18n( "Could not open %1\nReason: %2" ).arg( url().prettyURL( 0, KURL::StripFileProtocol ), d->lastErrorMessage ) );
 
1350
            }
 
1351
        }
 
1352
    }
 
1353
 
 
1354
    if ( importedFile != m_file )
 
1355
    {
 
1356
        // We opened a temporary file (result of an import filter)
 
1357
        // Set document URL to empty - we don't want to save in /tmp !
 
1358
        // But only if in readwrite mode (no saving problem otherwise)
 
1359
        // --
 
1360
        // But this isn't true at all.  If this is the result of an
 
1361
        // import, then importedFile=temporary_file.kwd and
 
1362
        // m_file/m_url=foreignformat.ext so m_url is correct!
 
1363
        // So don't resetURL() or else the caption won't be set when
 
1364
        // foreign files are opened (an annoying bug).
 
1365
        // - Clarence
 
1366
        //
 
1367
#if 0
 
1368
        if ( isReadWrite() )
 
1369
            resetURL();
 
1370
#endif
 
1371
 
 
1372
        // remove temp file - uncomment this to debug import filters
 
1373
        if(!importedFile.isEmpty())
 
1374
            unlink( QFile::encodeName(importedFile) );
 
1375
    }
 
1376
 
 
1377
    if ( ok && d->m_bSingleViewMode )
 
1378
    {
 
1379
        // See addClient below
 
1380
        KXMLGUIFactory* guiFactory = factory();
 
1381
        if( guiFactory ) // 0L when splitting views in konq, for some reason
 
1382
            guiFactory->removeClient( this );
 
1383
 
 
1384
        KoView *view = createView( d->m_wrapperWidget );
 
1385
        d->m_wrapperWidget->setKoView( view );
 
1386
        view->show();
 
1387
 
 
1388
        // Ok, now we have a view, so action() and domDocument() will work as expected
 
1389
        // -> rebuild GUI
 
1390
        if ( guiFactory )
 
1391
            guiFactory->addClient( this );
 
1392
    }
 
1393
 
 
1394
    if ( ok )
 
1395
    {
 
1396
        d->mimeType = typeName.latin1 ();
 
1397
 
 
1398
        d->outputMimeType = d->mimeType;
 
1399
 
 
1400
        const bool needConfirm = (d->mimeType != _native_format);
 
1401
        setConfirmNonNativeSave ( false, needConfirm  );
 
1402
        setConfirmNonNativeSave ( true, needConfirm );
 
1403
    }
 
1404
 
 
1405
    return ok;
858
1406
}
859
1407
 
860
1408
bool KoDocument::loadNativeFormat( const QString & file )
861
1409
{
862
 
  QApplication::setOverrideCursor( waitCursor );
863
 
 
864
 
  kdDebug(30003) << "KoDocument::loadNativeFormat( " << file << " )" << endl;
865
 
 
866
 
  QFile in(file);
867
 
  if ( !in.open( IO_ReadOnly ) )
868
 
  {
869
 
    QApplication::restoreOverrideCursor();
870
 
    return false;
871
 
  }
872
 
 
873
 
  // Try to find out whether it is a mime multi part file
874
 
  char buf[5];
875
 
  if ( in.readBlock( buf, 4 ) < 4 )
876
 
  {
877
 
    QApplication::restoreOverrideCursor();
878
 
    in.close();
879
 
    return false;
880
 
  }
881
 
 
882
 
  //kdDebug(30003) << "PATTERN=" << buf << endl;
883
 
 
884
 
  // Is it plain XML ?
885
 
  if ( strncasecmp( buf, "<?xm", 4 ) == 0 )
886
 
  {
887
 
    in.at(0);
888
 
    QDomDocument doc;
889
 
    doc.setContent( &in );
890
 
    bool res = loadXML( &in, doc );
891
 
    if ( res )
892
 
      res = completeLoading( 0L );
893
 
 
894
 
    QApplication::restoreOverrideCursor();
895
 
    in.close();
896
 
    m_bEmpty = false;
897
 
    return res;
898
 
  } else
899
 
  { // It's a koffice store (tar.gz)
900
 
    in.close();
901
 
    KoStore * store = new KoStore( file, KoStore::Read );
902
 
 
903
 
    if ( store->bad() )
904
 
    {
905
 
      delete store;
906
 
      QApplication::restoreOverrideCursor();
907
 
      return false;
908
 
    }
909
 
 
910
 
    if ( store->open( "root" ) )
911
 
    {
912
 
      KoStoreDevice dev( store );
913
 
      QDomDocument doc;
914
 
      doc.setContent( &dev );
915
 
      if ( !loadXML( &dev, doc ) )
916
 
      {
917
 
        delete store;
 
1410
    if ( !QFileInfo( file).isFile () )
 
1411
    {
 
1412
        d->lastErrorMessage = i18n( "%1 is not a file." ).arg(file);
 
1413
        return false;
 
1414
    }
 
1415
 
 
1416
    QApplication::setOverrideCursor( waitCursor );
 
1417
 
 
1418
    kdDebug(30003) << "KoDocument::loadNativeFormat( " << file << " )" << endl;
 
1419
 
 
1420
    QFile in;
 
1421
    bool isRawXML = false;
 
1422
    if ( d->m_specialOutputFlag != SaveAsDirectoryStore ) // Don't try to open a directory ;)
 
1423
    {
 
1424
        in.setName(file);
 
1425
        if ( !in.open( IO_ReadOnly ) )
 
1426
        {
 
1427
            QApplication::restoreOverrideCursor();
 
1428
            d->lastErrorMessage = i18n( "Couldn't open the file for reading (check read permissions)." );
 
1429
            return false;
 
1430
        }
 
1431
 
 
1432
        // Try to find out whether it is a mime multi part file
 
1433
        char buf[5];
 
1434
        if ( in.readBlock( buf, 4 ) < 4 )
 
1435
        {
 
1436
            QApplication::restoreOverrideCursor();
 
1437
            in.close();
 
1438
            d->lastErrorMessage = i18n( "Couldn't read the beginning of the file." );
 
1439
            return false;
 
1440
        }
 
1441
        isRawXML = (strncasecmp( buf, "<?xm", 4 ) == 0);
 
1442
        //kdDebug(30003) << "PATTERN=" << buf << endl;
 
1443
    }
 
1444
    // Is it plain XML?
 
1445
    if ( isRawXML )
 
1446
    {
 
1447
        in.at(0);
 
1448
        QString errorMsg;
 
1449
        int errorLine;
 
1450
        int errorColumn;
 
1451
        QDomDocument doc;
 
1452
        bool res;
 
1453
        if ( doc.setContent( &in , &errorMsg, &errorLine, &errorColumn ) )
 
1454
        {
 
1455
            res = loadXML( &in, doc );
 
1456
            if ( res )
 
1457
                res = completeLoading( 0L );
 
1458
        }
 
1459
        else
 
1460
        {
 
1461
            kdError (30003) << "Parsing Error! Aborting! (in KoDocument::loadNativeFormat (QFile))" << endl
 
1462
                            << "  Line: " << errorLine << " Column: " << errorColumn << endl
 
1463
                            << "  Message: " << errorMsg << endl;
 
1464
            d->lastErrorMessage = i18n( "parsing error in the main document at line %1, column %2\nError message: %3" )
 
1465
                                  .arg( errorLine ).arg( errorColumn ).arg( i18n ( "QXml", errorMsg.utf8() ) );
 
1466
            res=false;
 
1467
        }
 
1468
 
918
1469
        QApplication::restoreOverrideCursor();
919
 
        return false;
920
 
      }
921
 
      store->close();
 
1470
        in.close();
 
1471
        m_bEmpty = false;
 
1472
        return res;
922
1473
    } else
923
 
    {
924
 
      kdError(30003) << "ERROR: No maindoc.xml" << endl;
925
 
      delete store;
926
 
      QApplication::restoreOverrideCursor();
927
 
      return false;
928
 
    }
929
 
 
930
 
    if ( !loadChildren( store ) )
931
 
    {
932
 
      kdError(30003) << "ERROR: Could not load children" << endl;
933
 
      delete store;
934
 
      QApplication::restoreOverrideCursor();
935
 
      return false;
936
 
    }
937
 
    if ( store->open( "documentinfo.xml" ) )
938
 
    {
939
 
      KoStoreDevice dev( store );
940
 
      QDomDocument doc;
941
 
      doc.setContent( &dev );
942
 
      d->m_docInfo->load( doc );
943
 
      store->close();
944
 
    }
945
 
    else
946
 
    {
947
 
      kdDebug( 30003 ) << "cannot open document info" << endl;
948
 
      delete d->m_docInfo;
949
 
      d->m_docInfo = new KoDocumentInfo( this, "document info" );
950
 
    }
951
 
 
952
 
    bool res = completeLoading( store );
953
 
    delete store;
954
 
    QApplication::restoreOverrideCursor();
955
 
    m_bEmpty = false;
956
 
    return res;
957
 
  }
958
 
}
959
 
 
960
 
bool KoDocument::loadFromStore( KoStore* _store, const KURL & url )
961
 
{
962
 
  if ( _store->open( url.url() ) )
963
 
  {
964
 
    KoStoreDevice dev( _store );
965
 
    QDomDocument doc;
966
 
    doc.setContent( &dev );
967
 
    if ( !loadXML( &dev, doc ) )
968
 
      return false;
969
 
    _store->close();
970
 
  }
971
 
  // Store as document URL
972
 
  m_url = url;
973
 
 
974
 
  if ( !loadChildren( _store ) )
975
 
  {
976
 
    kdError(30003) << "ERROR: Could not load children" << endl;
977
 
    return false;
978
 
  }
979
 
 
980
 
  return completeLoading( _store );
981
 
}
 
1474
    { // It's a koffice store (tar.gz, zip, directory, etc.)
 
1475
        in.close();
 
1476
        KoStore::Backend backend = (d->m_specialOutputFlag == SaveAsDirectoryStore) ? KoStore::Directory : KoStore::Auto;
 
1477
        KoStore * store = KoStore::createStore( file, KoStore::Read, "", backend );
 
1478
 
 
1479
        if ( store->bad() )
 
1480
        {
 
1481
            d->lastErrorMessage = i18n( "Not a valid KOffice file." );
 
1482
            delete store;
 
1483
            QApplication::restoreOverrideCursor();
 
1484
            return false;
 
1485
        }
 
1486
 
 
1487
        if ( store->open( "root" ) )
 
1488
        {
 
1489
            QString errorMsg;
 
1490
            int errorLine;
 
1491
            int errorColumn;
 
1492
            QDomDocument doc;
 
1493
            if ( !doc.setContent( store->device(), &errorMsg, &errorLine, &errorColumn ) )
 
1494
            {
 
1495
                kdError (30003) << "Parsing Error! Aborting! (in KoDocument::loadNativeFormat (KoStore))" << endl
 
1496
                                << "  Line: " << errorLine << " Column: " << errorColumn << endl
 
1497
                                << "  Message: " << errorMsg << endl;
 
1498
                d->lastErrorMessage = i18n( "parsing error in the main document at line %1, column %2\nError message: %3" )
 
1499
                                      .arg( errorLine ).arg( errorColumn ).arg( i18n ( errorMsg.utf8() ) );
 
1500
                delete store;
 
1501
                QApplication::restoreOverrideCursor();
 
1502
                return false;
 
1503
            }
 
1504
            if ( !loadXML( store->device(), doc ) )
 
1505
            {
 
1506
                delete store;
 
1507
                QApplication::restoreOverrideCursor();
 
1508
                return false;
 
1509
            }
 
1510
            store->close();
 
1511
        } else
 
1512
        {
 
1513
            kdError(30003) << "ERROR: No maindoc.xml" << endl;
 
1514
            d->lastErrorMessage = i18n( "Invalid document: no file 'maindoc.xml'." );
 
1515
            delete store;
 
1516
            QApplication::restoreOverrideCursor();
 
1517
            return false;
 
1518
        }
 
1519
 
 
1520
        if ( !loadChildren( store ) )
 
1521
        {
 
1522
            kdError(30003) << "ERROR: Could not load children" << endl;
 
1523
// Proceed nonetheless
 
1524
#if 0
 
1525
            if ( d->lastErrorMessage.isEmpty() )
 
1526
                d->lastErrorMessage = i18n( "Couldn't load embedded objects." );
 
1527
            delete store;
 
1528
            QApplication::restoreOverrideCursor();
 
1529
            return false;
 
1530
#endif
 
1531
        }
 
1532
        if ( store->open( "documentinfo.xml" ) )
 
1533
        {
 
1534
            QDomDocument doc;
 
1535
            doc.setContent( store->device() );
 
1536
            d->m_docInfo->load( doc );
 
1537
            store->close();
 
1538
        }
 
1539
        else
 
1540
        {
 
1541
            //kdDebug( 30003 ) << "cannot open document info" << endl;
 
1542
            delete d->m_docInfo;
 
1543
            d->m_docInfo = new KoDocumentInfo( this, "document info" );
 
1544
        }
 
1545
 
 
1546
        bool res = completeLoading( store );
 
1547
        delete store;
 
1548
        QApplication::restoreOverrideCursor();
 
1549
        m_bEmpty = false;
 
1550
        return res;
 
1551
    }
 
1552
}
 
1553
 
 
1554
bool KoDocument::loadFromStore( KoStore* _store, const QString& url )
 
1555
{
 
1556
    if ( _store->open( url ) )
 
1557
    {
 
1558
        QDomDocument doc;
 
1559
        doc.setContent( _store->device() );
 
1560
        if ( !loadXML( _store->device(), doc ) )
 
1561
        {
 
1562
            _store->close();
 
1563
            return false;
 
1564
        }
 
1565
        _store->close();
 
1566
    }
 
1567
 
 
1568
    _store->pushDirectory();
 
1569
    // Store as document URL
 
1570
    if ( url.startsWith( STORE_PROTOCOL ) )
 
1571
        m_url = KURL( url );
 
1572
    else {
 
1573
        m_url = KURL( INTERNAL_PREFIX + url );
 
1574
        _store->enterDirectory( url );
 
1575
    }
 
1576
 
 
1577
    if ( !loadChildren( _store ) )
 
1578
    {
 
1579
        kdError(30003) << "ERROR: Could not load children" << endl;
 
1580
#if 0
 
1581
        return false;
 
1582
#endif
 
1583
    }
 
1584
 
 
1585
    bool result = completeLoading( _store );
 
1586
 
 
1587
    // Restore the "old" path
 
1588
    _store->popDirectory();
 
1589
 
 
1590
    return result;
 
1591
}
 
1592
 
 
1593
bool KoDocument::isInOperation()
 
1594
{
 
1595
    return d->m_numOperations > 0;
 
1596
}
 
1597
 
 
1598
void KoDocument::emitBeginOperation()
 
1599
{
 
1600
 
 
1601
    /* if we're already in an operation, don't send the signal again */
 
1602
    if (!isInOperation())
 
1603
        emit sigBeginOperation();
 
1604
    d->m_numOperations++;
 
1605
}
 
1606
 
 
1607
void KoDocument::emitEndOperation()
 
1608
{
 
1609
    d->m_numOperations--;
 
1610
 
 
1611
    /* don't end the operation till we've cleared all the nested operations */
 
1612
    if (d->m_numOperations == 0)
 
1613
        emit sigEndOperation();
 
1614
    else if (d->m_numOperations < 0)
 
1615
        /* ignore 'end' calls with no matching 'begin' call */
 
1616
        d->m_numOperations = 0;
 
1617
}
 
1618
 
982
1619
 
983
1620
bool KoDocument::isStoredExtern()
984
1621
{
985
 
  return ( m_url.protocol() != STORE_PROTOCOL );
 
1622
    return !storeInternal() && hasExternURL();
986
1623
}
987
1624
 
988
1625
void KoDocument::setModified( bool mod )
989
1626
{
990
 
    if ( mod == isModified() )
991
 
        return;
992
 
 
993
 
    kdDebug(30003) << "KoDocument::setModified( " << (mod ? "true" : "false") << ")" << endl;
 
1627
    //kdDebug(30003)<<k_funcinfo<<" url:" << m_url.path() << endl;
 
1628
    //kdDebug(30003)<<k_funcinfo<<" mod="<<mod<<" MParts mod="<<KParts::ReadWritePart::isModified()<<" isModified="<<isModified()<<endl;
 
1629
 
 
1630
    d->modifiedAfterAutosave=mod;
 
1631
    if ( isAutosaving() ) // ignore setModified calls due to autosaving
 
1632
        return;
 
1633
    if ( mod == KParts::ReadWritePart::isModified() )
 
1634
        return;
 
1635
 
994
1636
    KParts::ReadWritePart::setModified( mod );
995
1637
 
996
1638
    if ( mod )
998
1640
 
999
1641
    // This influences the title
1000
1642
    setTitleModified();
1001
 
    d->modifiedAfterAutosave=mod;
1002
 
 
 
1643
}
 
1644
 
 
1645
void KoDocument::setDoNotSaveExtDoc( bool on )
 
1646
{
 
1647
    d->m_doNotSaveExtDoc = on;
 
1648
}
 
1649
 
 
1650
int KoDocument::queryCloseDia()
 
1651
{
 
1652
    //kdDebug(30003)<<k_funcinfo<<endl;
 
1653
 
 
1654
    QString name;
 
1655
    if ( documentInfo() )
 
1656
    {
 
1657
        name = documentInfo()->title();
 
1658
    }
 
1659
    if ( name.isEmpty() )
 
1660
        name = url().fileName();
 
1661
 
 
1662
    if ( name.isEmpty() )
 
1663
        name = i18n( "Untitled" );
 
1664
 
 
1665
    int res = KMessageBox::warningYesNoCancel( 0L,
 
1666
                    i18n( "<p>The document <b>'%1'</b> has been modified.</p><p>Do you want to save it?</p>" ).arg(name));
 
1667
 
 
1668
    switch(res)
 
1669
    {
 
1670
        case KMessageBox::Yes :
 
1671
            setDoNotSaveExtDoc(); // Let save() only save myself and my internal docs
 
1672
            save(); // NOTE: External files always in native format. ###TODO: Handle non-native format
 
1673
            setModified( false ); // Now when queryClose() is called by closeEvent it won't do anything.
 
1674
            break;
 
1675
        case KMessageBox::No :
 
1676
            removeAutoSaveFiles();
 
1677
            setModified( false ); // Now when queryClose() is called by closeEvent it won't do anything.
 
1678
            break;
 
1679
        default : // case KMessageBox::Cancel :
 
1680
            return res; // cancels the rest of the files
 
1681
    }
 
1682
    return res;
 
1683
}
 
1684
 
 
1685
int KoDocument::queryCloseExternalChildren()
 
1686
{
 
1687
    //kdDebug(30003)<<k_funcinfo<<" checking for children in: "<<url().url()<<endl;
 
1688
    setDoNotSaveExtDoc(false);
 
1689
    QPtrListIterator<KoDocumentChild> it( children() );
 
1690
    for (; it.current(); ++it )
 
1691
    {
 
1692
        if ( !it.current()->isDeleted() )
 
1693
        {
 
1694
            KoDocument *doc = it.current()->document();
 
1695
            if ( doc )
 
1696
            {
 
1697
                if ( doc->isStoredExtern() ) //###TODO: Handle non-native mimetype docs
 
1698
                {
 
1699
                    if ( doc->isModified() )
 
1700
                    {
 
1701
                        kdDebug(30003)<<k_funcinfo<<" found modified child: "<<doc->url().url()<<" extern="<<doc->isStoredExtern()<<endl;
 
1702
                        if ( doc->queryCloseDia() == KMessageBox::Cancel )
 
1703
                            return  KMessageBox::Cancel;
 
1704
                    }
 
1705
                }
 
1706
                if ( doc->queryCloseExternalChildren() == KMessageBox::Cancel )
 
1707
                    return KMessageBox::Cancel;
 
1708
            }
 
1709
        }
 
1710
    }
 
1711
    return KMessageBox::Ok;
 
1712
}
 
1713
 
 
1714
void KoDocument::setTitleModified( const QString caption, bool mod )
 
1715
{
 
1716
    //kdDebug(30003)<<k_funcinfo<<" url: "<<url().url()<<" caption: "<<caption<<" mod: "<<mod<<endl;
 
1717
    KoDocument *doc = dynamic_cast<KoDocument *>( parent() );
 
1718
    if ( doc )
 
1719
    {
 
1720
        doc->setTitleModified( caption, mod );
 
1721
        return;
 
1722
    }
 
1723
    // we must be root doc so update caption in all related windows
 
1724
    QPtrListIterator<KoMainWindow> it( d->m_shells );
 
1725
    for (; it.current(); ++it )
 
1726
    {
 
1727
        it.current()->updateCaption( caption, mod );
 
1728
        it.current()->updateReloadFileAction(this);
 
1729
    }
1003
1730
}
1004
1731
 
1005
1732
void KoDocument::setTitleModified()
1006
1733
{
1007
 
    // Update caption in all related windows
1008
 
    QListIterator<KoMainWindow> it( d->m_shells );
1009
 
    for (; it.current(); ++it )
1010
 
        it.current()->updateCaption();
1011
 
}
 
1734
    //kdDebug(30003)<<k_funcinfo<<" url: "<<url().url()<<" extern: "<<isStoredExtern()<<" current: "<<d->m_current<<endl;
 
1735
    KoDocument *doc = dynamic_cast<KoDocument *>( parent() );
 
1736
    QString caption;
 
1737
    if ( (url().isEmpty() || isStoredExtern()) && d->m_current )
 
1738
    {
 
1739
        // Get caption from document info (title(), in about page)
 
1740
        if ( documentInfo() )
 
1741
        {
 
1742
            KoDocumentInfoPage * page = documentInfo()->page( QString::fromLatin1("about") );
 
1743
            if (page)
 
1744
                caption = static_cast<KoDocumentInfoAbout *>(page)->title();
 
1745
        }
 
1746
        if ( caption.isEmpty() )
 
1747
            caption = url().prettyURL( 0, KURL::StripFileProtocol );             // Fall back to document URL
1012
1748
 
1013
 
void KoDocument::changedByFilter( bool changed ) const
1014
 
{
1015
 
    kdDebug(30003) << "KoDocument::changedByFilter " << (changed ? "true" : "false") << ")" << endl;
1016
 
    d->m_changed=changed;
 
1749
        //kdDebug(30003)<<k_funcinfo<<" url: "<<url().url()<<" caption: "<<caption<<endl;
 
1750
        if ( doc )
 
1751
        {
 
1752
            doc->setTitleModified( caption, isModified() );
 
1753
            return;
 
1754
        }
 
1755
        else
 
1756
        {
 
1757
            // we must be root doc so update caption in all related windows
 
1758
            setTitleModified( caption, isModified() );
 
1759
            return;
 
1760
        }
 
1761
    }
 
1762
    if ( doc )
 
1763
    {
 
1764
        // internal doc or not current doc, so pass on the buck
 
1765
        doc->setTitleModified();
 
1766
    }
1017
1767
}
1018
1768
 
1019
1769
bool KoDocument::loadChildren( KoStore* )
1031
1781
    return true;
1032
1782
}
1033
1783
 
 
1784
QDomDocument KoDocument::createDomDocument( const QString& tagName, const QString& version ) const
 
1785
{
 
1786
    return createDomDocument( instance()->instanceName(), tagName, version );
 
1787
}
 
1788
 
 
1789
//static
 
1790
QDomDocument KoDocument::createDomDocument( const QString& appName, const QString& tagName, const QString& version )
 
1791
{
 
1792
    QDomImplementation impl;
 
1793
    QString url = QString("http://www.koffice.org/DTD/%1-%1.dtd").arg(appName).arg(version);
 
1794
    QDomDocumentType dtype = impl.createDocumentType( tagName,
 
1795
                                                      QString("-//KDE//DTD %1 %1//EN").arg(appName).arg(version),
 
1796
                                                      url );
 
1797
    // The namespace URN doesn't need to include the version number.
 
1798
    QString namespaceURN = QString("http://www.koffice.org/DTD/%1").arg(appName);
 
1799
    QDomDocument doc = impl.createDocument( namespaceURN, tagName, dtype );
 
1800
    doc.insertBefore( doc.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" ), doc.documentElement() );
 
1801
    return doc;
 
1802
}
 
1803
 
1034
1804
QDomDocument KoDocument::saveXML()
1035
1805
{
1036
1806
    kdError(30003) << "KoDocument::saveXML not implemented" << endl;
 
1807
    d->lastErrorMessage = i18n( "Internal error: saveXML not implemented" );
1037
1808
    return QDomDocument();
1038
1809
}
1039
1810
 
1040
1811
KService::Ptr KoDocument::nativeService()
1041
1812
{
1042
 
  if ( !m_nativeService )
1043
 
      m_nativeService = readNativeService( instance() );
 
1813
    if ( !m_nativeService )
 
1814
        m_nativeService = readNativeService( instance() );
1044
1815
 
1045
 
  return m_nativeService;
 
1816
    return m_nativeService;
1046
1817
}
1047
1818
 
1048
 
QCString KoDocument::nativeFormatMimeType()
 
1819
QCString KoDocument::nativeFormatMimeType() const
1049
1820
{
1050
 
    KService::Ptr service = nativeService();
 
1821
    KService::Ptr service = const_cast<KoDocument *>(this)->nativeService();
1051
1822
    if ( !service )
1052
1823
        return QCString();
1053
1824
    return service->property( "X-KDE-NativeMimeType" ).toString().latin1();
1056
1827
//static
1057
1828
KService::Ptr KoDocument::readNativeService( KInstance *instance )
1058
1829
{
1059
 
  QString instname = instance ? instance->instanceName() : kapp->instanceName();
1060
 
 
1061
 
  // Try by path first, so that we find the global one (which has the native mimetype)
1062
 
  // even if the user created a kword.desktop in ~/.kde/share/applnk or any subdir of it.
1063
 
  // If he created it under ~/.kde/share/applnk/Office/ then no problem anyway.
1064
 
  KService::Ptr service = KService::serviceByDesktopPath( QString::fromLatin1("Office/%1.desktop").arg(instname) );
1065
 
  if ( !service )
1066
 
  {
1067
 
    service = KService::serviceByDesktopName( instname );
1068
 
  }
1069
 
 
1070
 
  if ( !service )
 
1830
    QString instname = instance ? instance->instanceName() : kapp->instanceName();
 
1831
 
 
1832
    // The new way is: we look for a foopart.desktop in the kde_services dir.
 
1833
    QString servicepartname = instname + "part.desktop";
 
1834
    KService::Ptr service = KService::serviceByDesktopPath( servicepartname );
 
1835
    if ( service )
 
1836
        kdDebug(30003) << servicepartname << " found." << endl;
 
1837
    if ( !service )
 
1838
    {
 
1839
        // The old way is kept as fallback for compatibility, but in theory this is really never used anymore.
 
1840
 
 
1841
        // Try by path first, so that we find the global one (which has the native mimetype)
 
1842
        // even if the user created a kword.desktop in ~/.kde/share/applnk or any subdir of it.
 
1843
        // If he created it under ~/.kde/share/applnk/Office/ then no problem anyway.
 
1844
        service = KService::serviceByDesktopPath( QString::fromLatin1("Office/%1.desktop").arg(instname) );
 
1845
    }
 
1846
    if ( !service )
 
1847
        service = KService::serviceByDesktopName( instname );
 
1848
 
 
1849
#if KDE_IS_VERSION( 3, 1, 90 )
 
1850
    // workaround for 3.2-beta bug fixed in 3.2-final
 
1851
    if ( !service )
 
1852
        service = KService::serviceByStorageId( QString::fromLatin1( "kde-" ) + instname );
 
1853
#endif
 
1854
 
 
1855
    if ( !service )
 
1856
        return service; // not found
 
1857
 
 
1858
    // found, check that it's good
 
1859
    if ( service->property( "X-KDE-NativeMimeType" ).toString().isEmpty() )
 
1860
    {
 
1861
        // It may be that the servicetype "KOfficePart" is missing, which leads to this property not being known
 
1862
        if ( KServiceType::serviceType( "KOfficePart" ) == 0L )
 
1863
            kdError(30003) << "The serviceType KOfficePart is missing. Check that you have a kofficepart.desktop file in the share/servicetypes directory." << endl;
 
1864
        else if ( instname != "koshell" ) // hack for koshell
 
1865
            kdWarning(30003) << service->desktopEntryPath() << ": no X-KDE-NativeMimeType entry!" << endl;
 
1866
    }
 
1867
 
1071
1868
    return service;
1072
 
 
1073
 
  if ( service->property( "X-KDE-NativeMimeType" ).toString().isEmpty() )
1074
 
  {
1075
 
      // It may be that the servicetype "KOfficePart" is missing, which leads to this property not being known
1076
 
      if ( KServiceType::serviceType( "KOfficePart" ) == 0L )
1077
 
        kdError(30003) << "The serviceType KOfficePart is missing. Check that you have a kofficepart.desktop file in the share/servicetypes directory." << endl;
1078
 
      else if ( instname != "koshell" ) // hack for koshell
1079
 
        kdWarning(30003) << service->desktopEntryPath() << ": no X-KDE-NativeMimeType entry!" << endl;
1080
 
  }
1081
 
 
1082
 
  return service;
1083
1869
}
1084
1870
 
1085
1871
QCString KoDocument::readNativeFormatMimeType( KInstance *instance )
1092
1878
 
1093
1879
void KoDocument::addShell( KoMainWindow *shell )
1094
1880
{
1095
 
  d->m_shells.append( shell );
 
1881
    if ( d->m_shells.findRef( shell ) == -1 )
 
1882
    {
 
1883
        //kdDebug(30003) << "addShell: shell " << (void*)shell << " added to doc " << this << endl;
 
1884
        d->m_shells.append( shell );
 
1885
    }
1096
1886
}
1097
1887
 
1098
1888
void KoDocument::removeShell( KoMainWindow *shell )
1099
1889
{
1100
 
  d->m_shells.removeRef( shell );
 
1890
    //kdDebug(30003) << "removeShell: shell " << (void*)shell << " removed from doc " << this << endl;
 
1891
    d->m_shells.removeRef( shell );
1101
1892
}
1102
1893
 
1103
 
const QList<KoMainWindow>& KoDocument::shells() const
 
1894
const QPtrList<KoMainWindow>& KoDocument::shells() const
1104
1895
{
1105
1896
    return d->m_shells;
1106
1897
}
1112
1903
 
1113
1904
DCOPObject * KoDocument::dcopObject()
1114
1905
{
1115
 
  if ( !d->m_dcopObject )
1116
 
      d->m_dcopObject = new KoDocumentIface( this );
1117
 
  return d->m_dcopObject;
1118
 
}
1119
 
 
 
1906
    if ( !d->m_dcopObject )
 
1907
        d->m_dcopObject = new KoDocumentIface( this );
 
1908
    return d->m_dcopObject;
 
1909
}
 
1910
 
 
1911
QCString KoDocument::dcopObjectId() const
 
1912
{
 
1913
    return const_cast<KoDocument *>(this)->dcopObject()->objId();
 
1914
}
 
1915
 
 
1916
void KoDocument::setErrorMessage( const QString& errMsg )
 
1917
{
 
1918
    d->lastErrorMessage = errMsg;
 
1919
}
 
1920
 
 
1921
bool KoDocument::isAutosaving() // BIC: add const
 
1922
{
 
1923
    return d->m_autosaving;
 
1924
}
 
1925
 
 
1926
void KoDocument::removeAutoSaveFiles()
 
1927
{
 
1928
        // Eliminate any auto-save file
 
1929
        QString asf = autoSaveFile( m_file ); // the one in the current dir
 
1930
        if ( QFile::exists( asf ) )
 
1931
            unlink( QFile::encodeName( asf ) );
 
1932
        asf = autoSaveFile( QString::null ); // and the one in $HOME
 
1933
        if ( QFile::exists( asf ) )
 
1934
            unlink( QFile::encodeName( asf ) );
 
1935
}
 
1936
 
 
1937
void KoDocument::setBackupFile( bool _b )
 
1938
{
 
1939
    d->m_backupFile = _b;
 
1940
}
 
1941
 
 
1942
bool KoDocument::backupFile()const
 
1943
{
 
1944
    return d->m_backupFile;
 
1945
}
 
1946
 
 
1947
 
 
1948
void KoDocument::setBackupPath( const QString & _path)
 
1949
{
 
1950
    d->m_backupPath = _path;
 
1951
}
 
1952
 
 
1953
QString KoDocument::backupPath()const
 
1954
{
 
1955
    return d->m_backupPath;
 
1956
}
 
1957
 
 
1958
void KoDocument::setCurrent( bool on )
 
1959
{
 
1960
    //kdDebug(30003)<<k_funcinfo<<" url: "<<url().url()<<" set to: "<<on<<endl;
 
1961
    KoDocument *doc = dynamic_cast<KoDocument *>( parent() );
 
1962
    if ( doc )
 
1963
    {
 
1964
        if ( !isStoredExtern() )
 
1965
        {
 
1966
            // internal doc so set next external to current (for safety)
 
1967
            doc->setCurrent( true );
 
1968
            return;
 
1969
        }
 
1970
        // only externally stored docs shall have file name in title
 
1971
        d->m_current = on;
 
1972
        if ( !on )
 
1973
        {
 
1974
            doc->setCurrent( true );    // let my next external parent take over
 
1975
            return;
 
1976
        }
 
1977
        doc->forceCurrent( false ); // everybody else should keep off
 
1978
    }
 
1979
    else
 
1980
        d->m_current = on;
 
1981
 
 
1982
    setTitleModified();
 
1983
}
 
1984
 
 
1985
void KoDocument::forceCurrent( bool on )
 
1986
{
 
1987
    //kdDebug(30003)<<k_funcinfo<<" url: "<<url().url()<<" force to: "<<on<<endl;
 
1988
    d->m_current = on;
 
1989
    KoDocument *doc = dynamic_cast<KoDocument *>( parent() );
 
1990
    if ( doc )
 
1991
    {
 
1992
        doc->forceCurrent( false );
 
1993
    }
 
1994
}
 
1995
 
 
1996
bool KoDocument::isCurrent() const
 
1997
{
 
1998
    return d->m_current;
 
1999
}
 
2000
 
 
2001
bool KoDocument::storeInternal() const
 
2002
{
 
2003
    return d->m_storeInternal;
 
2004
}
 
2005
 
 
2006
void KoDocument::setStoreInternal( bool i )
 
2007
{
 
2008
    d->m_storeInternal = i;
 
2009
    //kdDebug(30003)<<k_funcinfo<<"="<<d->m_storeInternal<<" doc: "<<url().url()<<endl;
 
2010
}
 
2011
 
 
2012
bool KoDocument::hasExternURL()
 
2013
{
 
2014
    return !url().protocol().isEmpty() && url().protocol() != STORE_PROTOCOL && url().protocol() != INTERNAL_PROTOCOL;
 
2015
}
 
2016
 
 
2017
KoDocument::InitDocFlags KoDocument::initDocFlags() const
 
2018
{
 
2019
    return d->m_initDocFlags;
 
2020
}
 
2021
 
 
2022
void KoDocument::setInitDocFlags( InitDocFlags flags )
 
2023
{
 
2024
    d->m_initDocFlags = flags;
 
2025
}
 
2026
 
 
2027
void KoDocument::slotStarted( KIO::Job* job )
 
2028
{
 
2029
    if ( job )
 
2030
    {
 
2031
        job->setWindow( d->m_shells.current() );
 
2032
    }
 
2033
}
 
2034
 
 
2035
#include "koDocument_p.moc"
1120
2036
#include "koDocument.moc"
1121
 
#include "koDocument_p.moc"
1122
 
 
1123