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