1 /* This file is part of the KDE project
2  * Copyright (C) 2002-2006 David Faure <faure@kde.org>
3  * Copyright (C) 2005-2010 Thomas Zander <zander@kde.org>
4  * Copyright (C) 2007 Thorsten Zachmann <zachmann@kde.org>
5  * Copyright (C) 2008 Pierre Ducroquet <pinaraf@pinaraf.info>
6  * Copyright (C) 2008 Sebastian Sauer <mail@dipe.org>
7  * Copyright (C) 2010 Boudewijn Rempt <boud@kogmbh.com>
8  * Copyright (C) 2010 C. Boemann <cbo@kogmbh.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25 
26 #include "KWDocument.h"
27 
28 #include "KWFactory.h"
29 #include "KWView.h"
30 #include "KWCanvas.h"
31 #include "KWCanvasItem.h"
32 #include "KWPageManager.h"
33 #include "KWPage.h"
34 #include "KWPageStyle.h"
35 #include "KWOdfLoader.h"
36 #include "KWOdfWriter.h"
37 #include "frames/KWFrameSet.h"
38 #include "frames/KWTextFrameSet.h"
39 #include "frames/KWFrame.h"
40 #include "frames/KWFrameLayout.h"
41 #include "dialogs/KWFrameDialog.h"
42 #include "KWRootAreaProvider.h"
43 #include "WordsDebug.h"
44 
45 // calligra libs includes
46 #include <changetracker/KoChangeTracker.h>
47 #include <KoShapeManager.h>
48 #include <KoTextDocument.h>
49 #include <KoAnnotation.h>
50 #include <KoGridData.h>
51 #include <KoShapeAnchor.h>
52 #include <KoShapeContainer.h>
53 #include <KoToolManager.h>
54 #include <KoShapeController.h>
55 #include <KoShapeRegistry.h>
56 #include <KoShapeFactoryBase.h>
57 #include <KoStyleManager.h>
58 #include <KoDocumentResourceManager.h>
59 #include <KoCanvasResourceManager.h>
60 #include <KoTextRangeManager.h>
61 #include <KoInlineTextObjectManager.h>
62 #include <KoDocumentInfo.h>
63 #include <KoCharacterStyle.h>
64 #include <KoParagraphStyle.h>
65 #include <KoListStyle.h>
66 #include <KoListLevelProperties.h>
67 #include <KoSelection.h>
68 #include <KoTextDocumentLayout.h>
69 #include <KoTextLayoutRootArea.h>
70 #include <KoTextEditor.h>
71 #include <KoPart.h>
72 #include <KoDocumentInfoDlg.h>
73 #include <KoDocumentRdfBase.h>
74 #include <KoAnnotationLayoutManager.h>
75 #include <KoPageWidgetItem.h>
76 #include <KoUnit.h>
77 
78 #ifdef SHOULD_BUILD_RDF
79 #include <KoDocumentRdf.h>
80 #include <KoDocumentRdfEditWidget.h>
81 #endif
82 
83 #include <KoProgressUpdater.h>
84 #include <KoUpdater.h>
85 
86 // KF5
87 #include <klocalizedstring.h>
88 #include <kconfiggroup.h>
89 #include <KSharedConfig>
90 
91 // Qt
92 #include <QIODevice>
93 #include <QTimer>
94 #include <QThread>
95 #include <QCoreApplication>
96 #include <QTextBlock>
97 #include <QTime>
98 
KWDocument(KoPart * part)99 KWDocument::KWDocument(KoPart *part)
100         : KoDocument(part)
101         , m_isMasterDocument(false)
102         , m_frameLayout(&m_pageManager, m_frameSets)
103         , m_mainFramesetEverFinished(false)
104         , m_annotationManager(0)
105 {
106     Q_ASSERT(part);
107     m_frameLayout.setDocument(this);
108     resourceManager()->setOdfDocument(this);
109 
110     connect(&m_frameLayout, SIGNAL(newFrameSet(KWFrameSet*)), this, SLOT(addFrameSet(KWFrameSet*)));
111     connect(&m_frameLayout, SIGNAL(removedFrameSet(KWFrameSet*)), this, SLOT(removeFrameSet(KWFrameSet*)));
112 
113     // Init shape Factories with our frame based configuration panels.
114     m_panelFactories = KWFrameDialog::panels(this);
115     foreach (const QString &id, KoShapeRegistry::instance()->keys()) {
116         KoShapeFactoryBase *shapeFactory = KoShapeRegistry::instance()->value(id);
117         if (shapeFactory) {
118             shapeFactory->setOptionPanels(m_panelFactories);
119         }
120     }
121 
122     resourceManager()->setUndoStack(undoStack());
123     if (documentRdf()) {
124         documentRdf()->linkToResourceManager(resourceManager());
125     }
126 
127 #ifdef SHOULD_BUILD_RDF
128     {
129         KoDocumentRdf *rdf = new KoDocumentRdf(this);
130         setDocumentRdf(rdf);
131     }
132 
133 #endif
134 
135 
136 
137 /* TODO reenable after release
138     QVariant variant;
139     variant.setValue(new KoChangeTracker(resourceManager()));
140     resourceManager()->setResource(KoText::ChangeTracker, variant);
141 */
142     m_shapeController = new KoShapeController(0, this);
143 
144     if (inlineTextObjectManager()) {
145         connect(documentInfo(), SIGNAL(infoUpdated(QString,QString)),
146                 inlineTextObjectManager(), SLOT(documentInformationUpdated(QString,QString)));
147     }
148 
149     m_annotationManager = new KoAnnotationLayoutManager(this);
150 
151     clear();
152 }
153 
~KWDocument()154 KWDocument::~KWDocument()
155 {
156     qDeleteAll(m_panelFactories);
157     m_config.setUnit(unit());
158     saveConfig();
159     qDeleteAll(m_frameSets);
160 }
161 
isMasterDocument() const162 bool KWDocument::isMasterDocument() const
163 {
164     return m_isMasterDocument;
165 }
166 
setIsMasterDocument(bool isMasterDocument)167 void KWDocument::setIsMasterDocument(bool isMasterDocument)
168 {
169     m_isMasterDocument = isMasterDocument;
170 }
171 
172 
173 
174 // Words adds a couple of dialogs (like KWFrameDialog) which will not call addShape(), but
175 // will call addFrameSet.  Which will itself call addSequencedShape()
176 // any call coming in here is due to the undo/redo framework, pasting or for nested frames
addShape(KoShape * shape)177 void KWDocument::addShape(KoShape *shape)
178 {
179     KWFrame *frame = dynamic_cast<KWFrame*>(shape->applicationData());
180     debugWords << "shape=" << shape << "frame=" << frame;
181     if (frame == 0) {
182         if (shape->shapeId() == TextShape_SHAPEID) {
183             KWTextFrameSet *tfs = new KWTextFrameSet(this);
184             tfs->setName("Text");
185             frame = new KWFrame(shape, tfs);
186         } else {
187             KWFrameSet *fs = new KWFrameSet();
188             fs->setName(shape->shapeId());
189             frame = new KWFrame(shape, fs);
190         }
191     }
192     Q_ASSERT(KWFrameSet::from(shape));
193     if (!m_frameSets.contains(KWFrameSet::from(shape))) {
194         addFrameSet(KWFrameSet::from(shape));
195     }
196 
197     if (!(shape->shapeId() == "AnnotationTextShapeID")) {
198         emit shapeAdded(shape, KoShapeManager::PaintShapeOnAdd);
199     }
200 
201     shape->update();
202 }
203 
removeShape(KoShape * shape)204 void KWDocument::removeShape(KoShape *shape)
205 {
206     debugWords << "shape=" << shape;
207     KWFrameSet *fs = KWFrameSet::from(shape);
208     if (fs) { // not all shapes have to have to be in a frameset
209         if (fs->shapeCount() == 1) // last shape on FrameSet
210             removeFrameSet(fs); // shape and frameset will be deleted when the shape is deleted
211         else
212             fs->removeShape(shape);
213     } else { // not in a frameset, but we still have to remove it from views.
214         emit shapeRemoved(shape);
215     }
216     if (shape->shapeId() == "AnnotationTextShapeID") {
217         annotationLayoutManager()->removeAnnotationShape(shape);
218     }
219 }
220 
shapesRemoved(const QList<KoShape * > & shapes,KUndo2Command * command)221 void KWDocument::shapesRemoved(const QList<KoShape*> &shapes, KUndo2Command *command)
222 {
223     QMap<KoTextEditor *, QList<KoShapeAnchor *> > anchors;
224     QMap<KoTextEditor *, QList<KoAnnotation *> > annotations;
225     const KoAnnotationManager *annotationManager = textRangeManager()->annotationManager();
226     foreach (KoShape *shape, shapes) {
227         KoShapeAnchor *anchor = shape->anchor();
228         if (anchor && anchor->textLocation()) {
229             const QTextDocument *document = anchor->textLocation()->document();
230             if (document) {
231                 KoTextEditor *editor = KoTextDocument(document).textEditor();
232                 anchors[editor].append(anchor);
233             }
234             break;
235         }
236         foreach (const QString &name, annotationManager->annotationNameList()) {
237             KoAnnotation *annotation = annotationManager->annotation(name);
238             if (annotation->annotationShape() == shape) {
239                 // Remove From annotation layout manager.
240                 KoTextEditor *editor = KoTextDocument(annotation->document()).textEditor();
241                 annotations[editor].append(annotation);
242                 break;
243             }
244         }
245     }
246 
247     QMap<KoTextEditor *, QList<KoShapeAnchor *> >::const_iterator anchorIter(anchors.constBegin());
248     for (; anchorIter != anchors.constEnd(); ++anchorIter) {
249         anchorIter.key()->removeAnchors(anchorIter.value(), command);
250     }
251 
252     QMap<KoTextEditor *, QList<KoAnnotation *> >::const_iterator annotationIter(annotations.constBegin());
253     for (; annotationIter != annotations.constEnd(); ++annotationIter) {
254         annotationIter.key()->removeAnnotations(annotationIter.value(), command);
255     }
256 }
257 
generatePreview(const QSize & size)258 QPixmap KWDocument::generatePreview(const QSize &size)
259 {
260     // use first page as preview for all pages
261     KWPage firstPage = pageManager()->begin();
262     if (! firstPage.isValid()) {
263         // TODO: what to return for no page?
264         return QPixmap();
265     }
266 
267     // use shape manager from canvasItem even for QWidget environments
268     // if using the shape manager from one of the views there is no guarantee
269     // that the view, its canvas and the shapemanager is not destroyed in between
270     KoShapeManager* shapeManager = static_cast<KWCanvasItem*>(documentPart()->canvasItem(this))->shapeManager();
271 
272     return QPixmap::fromImage(firstPage.thumbnail(size, shapeManager, true));
273 }
274 
paintContent(QPainter &,const QRect &)275 void KWDocument::paintContent(QPainter &, const QRect &)
276 {
277 }
278 
insertPage(int afterPageNum,const QString & masterPageName)279 KWPage KWDocument::insertPage(int afterPageNum, const QString &masterPageName)
280 {
281     debugWords << "afterPageNum=" << afterPageNum << "masterPageName=" << masterPageName;
282 
283     //KWPage prevPage = m_document->pageManager().page(m_afterPageNum);
284     KWPageStyle pageStyle = pageManager()->pageStyle(masterPageName);
285     KWPage page = pageManager()->insertPage(afterPageNum + 1, pageStyle);
286     Q_ASSERT(page.isValid());
287     Q_ASSERT(page.pageNumber() >= 1 && page.pageNumber() <= pageManager()->pageCount());
288 
289     // Set the y-offset of the new page.
290     KWPage prevPage = page.previous();
291     if (prevPage.isValid()) {
292         KoInsets padding = pageManager()->padding();    //TODO Shouldn't this be style dependent ?
293         page.setOffsetInDocument(prevPage.offsetInDocument() + prevPage.height() + padding.top + padding.bottom);
294     } else {
295         page.setOffsetInDocument(0.0);
296     }
297 
298     debugWords << "pageNumber=" << page.pageNumber();
299 
300     // Create the KWTextFrame's for the new KWPage
301     KWFrameLayout *framelayout = frameLayout();
302     framelayout->createNewFramesForPage(page.pageNumber());
303 
304     // make sure we have updated the view before we do anything else
305     firePageSetupChanged();
306 
307     return page;
308 }
309 
appendPage(const QString & masterPageName)310 KWPage KWDocument::appendPage(const QString &masterPageName)
311 {
312     int number = 0;
313     KWPage last = m_pageManager.last();
314     if (last.isValid())
315         number = last.pageNumber();
316     return insertPage(number, masterPageName);
317 }
318 
firePageSetupChanged()319 void KWDocument::firePageSetupChanged()
320 {
321     debugWords;
322     if (inlineTextObjectManager())
323         inlineTextObjectManager()->setProperty(KoInlineObject::PageCount, pageCount());
324     emit pageSetupChanged();
325 }
326 
removeFrameSet(KWFrameSet * fs)327 void KWDocument::removeFrameSet(KWFrameSet *fs)
328 {
329     debugWords << "frameSet=" << fs;
330     m_frameSets.removeAt(m_frameSets.indexOf(fs));
331     setModified(true);
332     foreach (KoShape *shape, fs->shapes())
333         removeSequencedShape(shape);
334 
335     disconnect(fs, SIGNAL(shapeAdded(KoShape*)), this, SLOT(addSequencedShape(KoShape*)));
336     disconnect(fs, SIGNAL(shapeRemoved(KoShape*)), this, SLOT(removeSequencedShape(KoShape*)));
337 }
338 
relayout(QList<KWFrameSet * > framesets)339 void KWDocument::relayout(QList<KWFrameSet*> framesets)
340 {
341     if (framesets.isEmpty())
342         framesets = m_frameSets;
343 
344     debugWords << "frameSets=" << framesets;
345 
346 
347     // we switch to the interaction tool to avoid crashes if the tool was editing a frame.
348     //KoToolManager::instance()->switchToolRequested(KoInteractionTool_ID);
349 
350     // remove header/footer frames that are not visible.
351     //m_frameLayout.cleanupHeadersFooters();
352 
353     // create new frames and lay them out on the pages
354     foreach (const KWPage &page, m_pageManager.pages()) {
355         m_frameLayout.createNewFramesForPage(page.pageNumber());
356     }
357 
358     // re-layout the content displayed within the pages
359     foreach (KWFrameSet *fs, framesets) {
360         KWTextFrameSet *tfs = dynamic_cast<KWTextFrameSet*>(fs);
361         if (!tfs)
362             continue;
363         KoTextDocumentLayout *lay = dynamic_cast<KoTextDocumentLayout*>(tfs->document()->documentLayout());
364         Q_ASSERT(lay);
365 
366         if (tfs->textFrameSetType() == Words::MainTextFrameSet && m_layoutProgressUpdater) {
367             connect(lay, SIGNAL(layoutProgressChanged(int)), this, SLOT(layoutProgressChanged(int)));
368             connect(lay, SIGNAL(finishedLayout()), this, SLOT(layoutFinished()));
369         }
370 
371         // schedule all calls so multiple layout calls are compressed
372         lay->scheduleLayout();
373     }
374 
375     firePageSetupChanged();
376 }
377 
layoutProgressChanged(int percent)378 void KWDocument::layoutProgressChanged(int percent)
379 {
380     Q_ASSERT(m_layoutProgressUpdater);
381     m_layoutProgressUpdater->setProgress(percent);
382 }
383 
layoutFinished()384 void KWDocument::layoutFinished()
385 {
386     Q_ASSERT(m_layoutProgressUpdater);
387     disconnect(QObject::sender(), SIGNAL(layoutProgressChanged(int)), this, SLOT(layoutProgressChanged(int)));
388     disconnect(QObject::sender(), SIGNAL(finishedLayout()), this, SLOT(layoutFinished()));
389     m_layoutProgressUpdater->setProgress(100);
390     m_layoutProgressUpdater = 0; // free the instance
391 }
392 
addFrameSet(KWFrameSet * fs)393 void KWDocument::addFrameSet(KWFrameSet *fs)
394 {
395     debugWords << "frameSet=" << fs;
396 
397     Q_ASSERT(!m_frameSets.contains(fs));
398     setModified(true);
399 
400     // Be sure we add headers and footers to the beginning of the m_frameSets QList and every other KWFrameTextType
401     // after them so future operations iterating over that QList always handle headers and footers first.
402     int insertAt = m_frameSets.count();
403     KWTextFrameSet *tfs = dynamic_cast<KWTextFrameSet*>(fs);
404     if (tfs && Words::isHeaderFooter(tfs)) {
405         insertAt = 0;
406         for(int i = 0; i < m_frameSets.count(); ++i) {
407             KWTextFrameSet *_tfs = dynamic_cast<KWTextFrameSet*>(m_frameSets[i]);
408             if (_tfs && !Words::isHeaderFooter(_tfs)) {
409                 insertAt = i;
410                 break;
411             }
412         }
413     }
414     m_frameSets.insert(insertAt, fs);
415 
416     foreach (KoShape *shape, fs->shapes())
417         addSequencedShape(shape);
418 
419     if (KWTextFrameSet *tfs = dynamic_cast<KWTextFrameSet*>(fs)) {
420         Q_ASSERT(tfs->pageManager() == pageManager());
421         if (tfs->textFrameSetType() == Words::MainTextFrameSet) {
422             KoTextDocumentLayout *lay = dynamic_cast<KoTextDocumentLayout*>(tfs->document()->documentLayout());
423             Q_ASSERT(lay);
424             connect(lay, SIGNAL(finishedLayout()), this, SLOT(mainTextFrameSetLayoutDone()));
425         }
426     }
427 
428     connect(fs, SIGNAL(shapeAdded(KoShape*)), this, SLOT(addSequencedShape(KoShape*)));
429     connect(fs, SIGNAL(shapeRemoved(KoShape*)), this, SLOT(removeSequencedShape(KoShape*)));
430 }
431 
addSequencedShape(KoShape * shape)432 void KWDocument::addSequencedShape(KoShape *shape)
433 {
434     debugWords << "shape=" << shape << "frameSet=" << KWFrameSet::from(shape);
435     //firePageSetupChanged();
436     emit shapeAdded(shape, KoShapeManager::AddWithoutRepaint);
437 }
438 
removeSequencedShape(KoShape * shape)439 void KWDocument::removeSequencedShape(KoShape *shape)
440 {
441     debugWords << "shape=" << shape << "frameSet=" << KWFrameSet::from(shape);
442 
443     emit shapeRemoved(shape);
444     KWPage page = pageManager()->page(shape);
445     if (!page.isValid()) return;
446     if (!page.isAutoGenerated()) return;
447     if (page != pageManager()->last() || page == pageManager()->begin())
448         return; // can only delete last page.
449     foreach (KWFrameSet *fs, m_frameSets) {
450         foreach (KoShape *s, fs->shapes()) {
451             if (page == pageManager()->page(s))
452                 return;
453         }
454     }
455     //KWPageRemoveCommand *cmd = new KWPageRemoveCommand(this, page);
456     //cmd->redo();
457     //delete cmd;
458 }
459 
mainTextFrameSetLayoutDone()460 void KWDocument::mainTextFrameSetLayoutDone()
461 {
462     m_mainFramesetEverFinished = true;
463 }
464 
frameSetByName(const QString & name)465 KWFrameSet *KWDocument::frameSetByName(const QString &name)
466 {
467     foreach (KWFrameSet *fs, m_frameSets) {
468         if (fs->name() == name)
469             return fs;
470     }
471     return 0;
472 }
473 
mainFrameSet() const474 KWTextFrameSet *KWDocument::mainFrameSet() const
475 {
476     return m_frameLayout.mainFrameSet();
477 }
478 
inlineTextObjectManager() const479 KoInlineTextObjectManager *KWDocument::inlineTextObjectManager() const
480 {
481     QVariant var = resourceManager()->resource(KoText::InlineTextObjectManager);
482     return var.value<KoInlineTextObjectManager*>();
483 }
484 
textRangeManager() const485 KoTextRangeManager *KWDocument::textRangeManager() const
486 {
487     QVariant var = resourceManager()->resource(KoText::TextRangeManager);
488     return var.value<KoTextRangeManager*>();
489 }
490 
uniqueFrameSetName(const QString & suggestion)491 QString KWDocument::uniqueFrameSetName(const QString &suggestion)
492 {
493     // make up a new name for the frameset, use "[base] [digits]" as template.
494     // Fully translatable naturally :)
495     return renameFrameSet("", suggestion);
496 }
497 
suggestFrameSetNameForCopy(const QString & base)498 QString KWDocument::suggestFrameSetNameForCopy(const QString &base)
499 {
500     // make up a new name for the frameset, use Copy[digits]-[base] as template.
501     // Fully translatable naturally :)
502     return renameFrameSet(i18n("Copy"), base);
503 }
504 
renameFrameSet(const QString & prefix,const QString & base)505 QString KWDocument::renameFrameSet(const QString &prefix, const QString &base)
506 {
507     if (! frameSetByName(base))
508         return base;
509     QString before, after;
510     QRegExp findDigits("\\d+");
511     int pos = findDigits.indexIn(base);
512     if (pos >= 0) {
513         before = base.left(pos);
514         after = base.mid(pos + findDigits.matchedLength());
515     } else if (prefix.isEmpty())
516         before = base + ' ';
517     else {
518         before = prefix;
519         after = ' ' + base;
520     }
521 
522     if (! before.startsWith(prefix)) {
523         before = prefix + before;
524     }
525 
526     int count = 0;
527     while (true) {
528         QString name = QString(before + (count == 0 ? QString() : QString::number(count)) + after).trimmed();
529         if (! frameSetByName(name))
530             return name;
531         count++;
532     }
533 }
534 
535 // *** LOADING
536 
initEmpty()537 void KWDocument::initEmpty()
538 {
539     clear();
540 
541     appendPage("Standard");
542 
543     Q_ASSERT(resourceManager()->hasResource(KoText::StyleManager));
544     KoStyleManager *styleManager = resourceManager()->resource(KoText::StyleManager).value<KoStyleManager*>();
545     Q_ASSERT(styleManager);
546     KoParagraphStyle *parag = new KoParagraphStyle();
547     parag->setName(i18n("Standard"));
548     parag->setFontPointSize(12);
549     parag->setFontWeight(QFont::Normal);
550     styleManager->add(parag);
551 
552     parag = new KoParagraphStyle();
553     parag->setName(i18n("Document Title"));
554     parag->setFontPointSize(24);
555     parag->setFontWeight(QFont::Bold);
556     parag->setAlignment(Qt::AlignCenter);
557     styleManager->add(parag);
558 
559     parag = new KoParagraphStyle();
560     parag->setName(i18n("Head 1"));
561     parag->setFontPointSize(20);
562     parag->setFontWeight(QFont::Bold);
563     styleManager->add(parag);
564 
565     parag = new KoParagraphStyle();
566     parag->setName(i18n("Head 2"));
567     parag->setFontPointSize(16);
568     parag->setFontWeight(QFont::Bold);
569     styleManager->add(parag);
570 
571     parag = new KoParagraphStyle();
572     parag->setName(i18n("Head 3"));
573     parag->setFontPointSize(12);
574     parag->setFontWeight(QFont::Bold);
575     styleManager->add(parag);
576 
577     parag = new KoParagraphStyle();
578     parag->setName(i18n("Bullet List"));
579     KoListStyle *list = new KoListStyle(parag);
580     KoListLevelProperties llp = list->levelProperties(0);
581     llp.setLabelType(KoListStyle::BulletCharLabelType);
582     llp.setBulletCharacter(QChar(0x2022)); // Bullet
583     list->setLevelProperties(llp);
584     parag->setListStyle(list);
585     styleManager->add(parag);
586 
587     setMimeTypeAfterLoading("application/vnd.oasis.opendocument.text");
588     KoDocument::initEmpty();
589     clearUndoHistory();
590 }
591 
clear()592 void KWDocument::clear()
593 {
594     // document defaults
595     foreach (const KWPage &page, m_pageManager.pages())
596         m_pageManager.removePage(page);
597     m_pageManager.clearPageStyles();
598 
599     m_config.load(this); // re-load values
600     foreach (KWFrameSet *fs, m_frameSets) {
601         removeFrameSet(fs);
602         delete fs;
603     }
604 
605     // industry standard for bleed
606     KoInsets padding;
607     padding.top = MM_TO_POINT(3);
608     padding.bottom = MM_TO_POINT(3);
609     padding.left = MM_TO_POINT(3);
610     padding.right = MM_TO_POINT(3);
611     m_pageManager.setPadding(padding);
612 
613     if (inlineTextObjectManager())
614         inlineTextObjectManager()->setProperty(KoInlineObject::PageCount, pageCount());
615 }
616 
setupOpenFileSubProgress()617 void KWDocument::setupOpenFileSubProgress()
618 {
619     if (progressUpdater()) {
620         m_layoutProgressUpdater = progressUpdater()->startSubtask(1, "Layouting");
621     }
622 }
623 
loadOdf(KoOdfReadStore & odfStore)624 bool KWDocument::loadOdf(KoOdfReadStore &odfStore)
625 {
626     clear();
627     KWOdfLoader loader(this);
628     bool rc = loader.load(odfStore);
629     if (rc)
630         endOfLoading();
631     return rc;
632 }
633 
loadXML(const KoXmlDocument & doc,KoStore * store)634 bool KWDocument::loadXML(const KoXmlDocument &doc, KoStore *store)
635 {
636     Q_UNUSED(doc);
637     Q_UNUSED(store);
638     return false;
639 }
640 
endOfLoading()641 void KWDocument::endOfLoading() // called by both oasis and oldxml
642 {
643     debugWords;
644 
645     // Get the master page name of the first page.
646     QString firstPageMasterName;
647     if (mainFrameSet()) {
648         QTextBlock block = mainFrameSet()->document()->firstBlock();
649         firstPageMasterName = block.blockFormat().stringProperty(KoParagraphStyle::MasterPageName);
650     }
651 
652     appendPage(firstPageMasterName);
653 
654     relayout();
655 
656     debugWords << "KWDocument::endOfLoading done";
657 #if 0
658     // Note that more stuff will happen in completeLoading
659     firePageSetupChanged();
660 #endif
661     setModified(false);
662 }
663 
saveOdf(SavingContext & documentContext)664 bool KWDocument::saveOdf(SavingContext &documentContext)
665 {
666     KWOdfWriter writer(this);
667     return writer.save(documentContext.odfStore, documentContext.embeddedSaver);
668 }
669 
updatePagesForStyle(const KWPageStyle & style)670 void KWDocument::updatePagesForStyle(const KWPageStyle &style)
671 {
672     debugWords << "pageStyleName=" << style.name();
673     QList<KWFrameSet*> framesets;
674     foreach(KWFrameSet *fs, frameLayout()->getFrameSets(style)) {
675         KWTextFrameSet* tfs = dynamic_cast<KWTextFrameSet*>(fs);
676         if (tfs)
677             framesets.append(tfs);
678     }
679     int pageNumber = -1;
680     foreach (const KWPage &page, pageManager()->pages()) {
681         if (page.pageStyle() == style) {
682             pageNumber = page.pageNumber();
683             break;
684         }
685     }
686     //Q_ASSERT(pageNumber >= 1);
687     if (pageNumber < 1)
688         return;
689     foreach(KWFrameSet *fs, framesets) {
690         static_cast<KWTextFrameSet*>(fs)->rootAreaProvider()->clearPages(pageNumber);
691     }
692     relayout(framesets);
693 }
694 
saveConfig()695 void KWDocument::saveConfig()
696 {
697 //   KConfigGroup group(KoGlobal::calligraConfig(), "Spelling");
698 //   group.writeEntry("PersonalDict", m_spellCheckPersonalDict);
699 
700     m_config.save();
701     KSharedConfigPtr config = KSharedConfig::openConfig();
702     KConfigGroup interface = config->group("Interface");
703     interface.writeEntry("ResolutionX", gridData().gridX());
704     interface.writeEntry("ResolutionY", gridData().gridY());
705 }
706 
findTargetTextShape(KoShape * shape) const707 KoShape *KWDocument::findTargetTextShape(KoShape *shape) const
708 {
709     KoShape *result = 0;
710     int      area   = 0;
711     QRectF   br     = shape->boundingRect();
712 
713     // now find the frame that is closest to the frame we want to inline.
714     foreach (KoShape *shape, mainFrameSet()->shapes()) {
715         QRectF intersection  = br.intersected(shape->boundingRect());
716         int    intersectArea = qRound(intersection.width() * intersection.height());
717 
718         if (intersectArea > area) {
719             result = shape;
720             area   = intersectArea;
721         } else if (result == 0) {
722             // TODO check distance between frames or something.
723         }
724     }
725 
726     return result;
727 }
728 
anchorOfShape(KoShape * shape) const729 KoShapeAnchor* KWDocument::anchorOfShape(KoShape *shape) const
730 {
731     Q_ASSERT(mainFrameSet());
732     Q_ASSERT(shape);
733 
734     KoShapeAnchor *anchor = shape->anchor();
735 
736     if (!anchor) {
737         anchor = new KoShapeAnchor(shape);
738         anchor->setAnchorType(KoShapeAnchor::AnchorPage);
739         anchor->setHorizontalPos(KoShapeAnchor::HFromLeft);
740         anchor->setVerticalPos(KoShapeAnchor::VFromTop);
741         shape->setAnchor(anchor);
742     }
743 
744     return anchor;
745 }
746 
747 
frameOfShape(KoShape * shape) const748 KWFrame *KWDocument::frameOfShape(KoShape* shape) const
749 {
750     while (shape) {
751         KWFrame *answer = dynamic_cast<KWFrame*>(shape->applicationData());
752         if (answer)
753             return answer;
754         if (shape->parent() == 0)
755             break;
756         shape = shape->parent();
757     }
758 
759     KWFrame *answer = dynamic_cast<KWFrame*>(shape->applicationData());
760     if (answer == 0) { // this may be a clipping shape containing the frame-shape
761         KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
762         if (container && container->shapeCount() == 1) {
763             answer = dynamic_cast<KWFrame*>(container->shapes()[0]->applicationData());
764         }
765     }
766 
767     return answer;
768 }
769 
createDocumentInfoDialog(QWidget * parent,KoDocumentInfo * docInfo) const770 KoDocumentInfoDlg *KWDocument::createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const
771 {
772 
773     KoDocumentInfoDlg *dlg = new KoDocumentInfoDlg(parent, docInfo);
774     KoMainWindow *mainwin = dynamic_cast<KoMainWindow*>(parent);
775     if (mainwin) {
776         connect(dlg, SIGNAL(saveRequested()), mainwin, SLOT(slotFileSave()));
777     }
778 
779 #ifdef SHOULD_BUILD_RDF
780     KoPageWidgetItem *rdfEditWidget = new KoDocumentRdfEditWidget(static_cast<KoDocumentRdf*>(documentRdf()));
781     dlg->addPageItem(rdfEditWidget);
782 #endif
783     return dlg;
784 }
785