1 /* This file is part of the KDE project
2  * Copyright (C) 2006 Thomas Zander <zander@kde.org>
3  * Copyright (C) 2008 Thorsten Zachmann <zachmann@kde.org>
4  * Copyright (C) 2008 Girish Ramakrishnan <girish@forwardbias.in>
5  * Copyright (C) 2009 Pierre Stirnweiss <pstirnweiss@googlemail.com>
6  * Copyright (C) 2010 Benjamin Port <port.benjamin@gmail.com>
7  * Copyright (C) 2011 Pierre Ducroquet <pinaraf@pinaraf.info>
8  * Copyright (C) 2011 Boudewijn Rempt <boud@valdyas.org>
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 "KoTextWriter.h"
27 
28 #include <KoTextWriter_p.h>
29 #include <KoStyleManager.h>
30 #include <KoParagraphStyle.h>
31 #include <KoTextDocument.h>
32 #include <KoShapeSavingContext.h>
33 #include <KoGenStyles.h>
34 #include <opendocument/KoTextSharedSavingData.h>
35 
36 #include <QTextList>
37 #include <QTextTableCell>
38 
39 #include "TextDebug.h"
40 
KoTextWriter(KoShapeSavingContext & context,KoDocumentRdfBase * rdfData)41 KoTextWriter::KoTextWriter(KoShapeSavingContext &context, KoDocumentRdfBase *rdfData)
42     : d(new Private(context))
43 {
44     d->rdfData = rdfData;
45     KoSharedSavingData *sharedData = context.sharedData(KOTEXT_SHARED_SAVING_ID);
46     if (sharedData) {
47         d->sharedData = dynamic_cast<KoTextSharedSavingData *>(sharedData);
48     }
49 
50     if (!d->sharedData) {
51         d->sharedData = new KoTextSharedSavingData();
52         if (!sharedData) {
53             context.addSharedData(KOTEXT_SHARED_SAVING_ID, d->sharedData);
54         } else {
55             warnText << "A different type of sharedData was found under the" << KOTEXT_SHARED_SAVING_ID;
56             Q_ASSERT(false);
57         }
58     }
59 }
60 
~KoTextWriter()61 KoTextWriter::~KoTextWriter()
62 {
63     delete d;
64 }
65 
saveOdf(KoShapeSavingContext & context,KoDocumentRdfBase * rdfData,QTextDocument * document,int from,int to)66 void KoTextWriter::saveOdf(KoShapeSavingContext &context, KoDocumentRdfBase *rdfData, QTextDocument *document, int from, int to)
67 {
68     KoTextWriter writer(context, rdfData);
69     writer.write(document, from, to);
70 }
71 
saveParagraphStyle(const QTextBlock & block,KoStyleManager * styleManager,KoShapeSavingContext & context)72 QString KoTextWriter::saveParagraphStyle(const QTextBlock &block, KoStyleManager *styleManager, KoShapeSavingContext &context)
73 {
74     QTextBlockFormat blockFormat = block.blockFormat();
75     QTextCharFormat charFormat = QTextCursor(block).blockCharFormat();
76     return saveParagraphStyle(blockFormat, charFormat, styleManager, context);
77 }
78 
saveParagraphStyle(const QTextBlockFormat & blockFormat,const QTextCharFormat & charFormat,KoStyleManager * styleManager,KoShapeSavingContext & context)79 QString KoTextWriter::saveParagraphStyle(const QTextBlockFormat &blockFormat, const QTextCharFormat &charFormat, KoStyleManager * styleManager, KoShapeSavingContext &context)
80 {
81     KoParagraphStyle *defaultParagraphStyle = styleManager->defaultParagraphStyle();
82     KoParagraphStyle *originalParagraphStyle = styleManager->paragraphStyle(blockFormat.intProperty(KoParagraphStyle::StyleId));
83     if (!originalParagraphStyle)
84         originalParagraphStyle = defaultParagraphStyle;
85 
86     QString generatedName;
87     QString displayName = originalParagraphStyle->name();
88     QString internalName = QString(QUrl::toPercentEncoding(displayName, "", " ")).replace('%', '_');
89 
90     // we'll convert the blockFormat to a KoParagraphStyle to check for local changes.
91     KoParagraphStyle paragStyle(blockFormat, charFormat);
92     if (paragStyle == (*originalParagraphStyle)) { // This is the real, unmodified character style.
93         // TODO zachmann: this could use the name of the saved style without saving it again
94         // therefore we would need to store that information in the saving context
95         if (originalParagraphStyle != defaultParagraphStyle) {
96             KoGenStyle style(KoGenStyle::ParagraphStyle, "paragraph");
97             originalParagraphStyle->saveOdf(style, context);
98             generatedName = context.mainStyles().insert(style, internalName, KoGenStyles::DontAddNumberToName);
99         }
100     } else { // There are manual changes... We'll have to store them then
101         KoGenStyle style(KoGenStyle::ParagraphAutoStyle, "paragraph", internalName);
102         if (context.isSet(KoShapeSavingContext::AutoStyleInStyleXml))
103             style.setAutoStyleInStylesDotXml(true);
104         if (originalParagraphStyle) {
105             paragStyle.removeDuplicates(*originalParagraphStyle);
106             paragStyle.setParentStyle(originalParagraphStyle);
107         }
108         paragStyle.saveOdf(style, context);
109         generatedName = context.mainStyles().insert(style, "P");
110     }
111     return generatedName;
112 }
113 
write(const QTextDocument * document,int from,int to)114 void KoTextWriter::write(const QTextDocument *document, int from, int to)
115 {
116     d->document = const_cast<QTextDocument*>(document);
117     d->styleManager = KoTextDocument(document).styleManager();
118 
119     QTextBlock fromblock = document->findBlock(from);
120     QTextBlock toblock = document->findBlock(to);
121 
122     QTextCursor fromcursor(fromblock);
123 
124     QTextList *currentList = fromcursor.currentList();
125 
126     // NOTE even better would be if we create a new list out of multiple selected
127     // listitems that contain only the selected items. But following
128     // at least enables copying a whole list while still being able to copy/paste
129     // only parts of the text within a list (see also bug 275990).
130     // NOTE this has been fixed for tables now, and it looks like the list code is seriously wrong
131     // not just like the table code was, but more fundamentally as lists in qt is an orthogonal concept
132     if (currentList) {
133         if (from == 0 && to < 0) {
134             // save everything means also save current table and list
135             currentList = 0;
136         } else {
137             QTextCursor toCursor(toblock);
138             toCursor.setPosition(to, QTextCursor::KeepAnchor);
139 
140             if (!fromcursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor)) {
141                 fromcursor = QTextCursor();
142             }
143             if (!toCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor)) {
144                 toCursor = QTextCursor();
145             }
146 
147             // save the whole list if all list-items are selected
148             int fromindex = currentList->itemNumber(fromblock);
149             int toindex = currentList->itemNumber(toblock);
150             if ((fromcursor.isNull() || fromcursor.currentList() != currentList) &&
151                 (toCursor.isNull() || toCursor.currentList() != currentList) &&
152                 fromindex <= 0 && (toindex < 0 || toindex == currentList->count()-1)
153                 ) {
154                 currentList = 0;
155             }
156         }
157     }
158 
159     QHash<QTextList *, QString> listStyles = d->saveListStyles(fromblock, to);
160     d->globalFrom = from;
161     d->globalTo = to;
162     d->writeBlocks(const_cast<QTextDocument *>(document), from, to, listStyles, 0, currentList);
163 }
164