File indexing completed on 2024-05-12 16:35:02
0001 /* This file is part of the KDE project 0002 * Copyright (C) 2011 Sebastian Sauer <mail@dipe.org> 0003 * 0004 * This library is free software; you can redistribute it and/or 0005 * modify it under the terms of the GNU Library General Public 0006 * License as published by the Free Software Foundation; either 0007 * version 2 of the License, or (at your option) any later version. 0008 * 0009 * This library is distributed in the hope that it will be useful, 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 * Library General Public License for more details. 0013 * 0014 * You should have received a copy of the GNU Library General Public License 0015 * along with this library; see the file COPYING.LIB. If not, write to 0016 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 * Boston, MA 02110-1301, USA. 0018 */ 0019 0020 #include "ChapterVariable.h" 0021 0022 #include "VariablesDebug.h" 0023 0024 #include <KoXmlReader.h> 0025 #include <KoXmlWriter.h> 0026 #include <KoProperties.h> 0027 #include <KoShape.h> 0028 #include <KoShapeSavingContext.h> 0029 #include <KoShapeLoadingContext.h> 0030 #include <KoXmlNS.h> 0031 #include <KoTextLayoutRootArea.h> 0032 #include <KoTextDocumentLayout.h> 0033 #include <KoParagraphStyle.h> 0034 #include <KoTextBlockData.h> 0035 0036 #include <klocalizedstring.h> 0037 0038 #include <QFontMetricsF> 0039 #include <QTextDocument> 0040 #include <QAbstractTextDocumentLayout> 0041 #include <QTextInlineObject> 0042 #include <QLabel> 0043 #include <QComboBox> 0044 #include <QGridLayout> 0045 #include <QSpinBox> 0046 0047 ChapterVariable::ChapterVariable() 0048 : KoVariable(true) 0049 , m_format(ChapterName) 0050 , m_level(1) 0051 { 0052 } 0053 0054 void ChapterVariable::readProperties(const KoProperties *props) 0055 { 0056 m_format = (FormatTypes) props->intProperty("format"); 0057 m_level = qMax(1, props->intProperty("level")); 0058 } 0059 0060 void ChapterVariable::resize(const QTextDocument *_document, QTextInlineObject &object, int _posInDocument, const QTextCharFormat &format, QPaintDevice *pd) 0061 { 0062 QTextDocument *document = const_cast<QTextDocument*>(_document); 0063 int posInDocument = _posInDocument; 0064 bool checkBackwards = true; 0065 QTextFrame::iterator startIt, endIt; 0066 0067 KoTextDocumentLayout *lay = qobject_cast<KoTextDocumentLayout*>(document->documentLayout()); 0068 KoTextDocumentLayout *ref = lay->referencedLayout(); 0069 if (ref) { 0070 KoTextLayoutRootArea *rootArea = lay->rootAreaForPosition(posInDocument); 0071 if (!rootArea) { 0072 KoVariable::resize(_document, object, _posInDocument, format, pd); 0073 return; // not ready yet 0074 } 0075 KoTextPage *page = rootArea->page(); 0076 if (!page) { 0077 KoVariable::resize(_document, object, _posInDocument, format, pd); 0078 return; // should not happen 0079 } 0080 int pagenumber = page->pageNumber(); 0081 foreach(KoTextLayoutRootArea *a, ref->rootAreas()) { 0082 KoTextPage * p = a->page(); 0083 if (!p || p->pageNumber() != pagenumber) 0084 continue; 0085 startIt = a->startTextFrameIterator(); 0086 endIt = a->endTextFrameIterator(); 0087 if (startIt.currentBlock().isValid()) 0088 posInDocument = startIt.currentBlock().position(); 0089 else if (startIt.currentFrame()) 0090 posInDocument = startIt.currentFrame()->firstCursorPosition().position(); 0091 else // abort 0092 break; 0093 document = ref->document(); 0094 checkBackwards = false; // check forward 0095 break; 0096 } 0097 if (document == _document) { 0098 KoVariable::resize(_document, object, _posInDocument, format, pd); 0099 return; // should not happen 0100 } 0101 } 0102 0103 QTextBlock block = document->findBlock(posInDocument); 0104 while (block.isValid()) { 0105 if (block.blockFormat().hasProperty(KoParagraphStyle::OutlineLevel)) { 0106 int level = block.blockFormat().intProperty(KoParagraphStyle::OutlineLevel); 0107 if (level == m_level) { 0108 KoTextBlockData data(block); 0109 switch(m_format) { 0110 case ChapterName: 0111 setValue(block.text()); 0112 break; 0113 case ChapterNumber: 0114 setValue(data.counterText()); 0115 break; 0116 case ChapterNumberName: 0117 setValue(QString("%1 %2").arg(data.counterText()).arg(block.text())); 0118 break; 0119 case ChapterPlainNumber: 0120 setValue(data.counterPlainText()); 0121 break; 0122 case ChapterPlainNumberName: 0123 setValue(QString("%1 %2").arg(data.counterPlainText()).arg(block.text())); 0124 break; 0125 default: 0126 break; 0127 } 0128 break; // job done 0129 } 0130 } 0131 0132 if (checkBackwards) { 0133 block = block.previous(); 0134 } else { 0135 block = block.next(); 0136 0137 // If we search forwards and reached the end of the page then we continue searching backwards 0138 // at the beginning of the page. 0139 if (!block.isValid() || 0140 (endIt.currentBlock().isValid() && block.position() > endIt.currentBlock().position()) || 0141 (endIt.currentFrame() && block.position() > endIt.currentFrame()->firstCursorPosition().block().position()) ) { 0142 if (startIt.currentBlock().isValid()) 0143 block = startIt.currentBlock(); 0144 else if (startIt.currentFrame()) 0145 block = startIt.currentFrame()->firstCursorPosition().block(); 0146 else // abort 0147 break; 0148 checkBackwards = true; 0149 } 0150 } 0151 } 0152 0153 KoVariable::resize(_document, object, _posInDocument, format, pd); 0154 } 0155 0156 void ChapterVariable::saveOdf(KoShapeSavingContext &context) 0157 { 0158 KoXmlWriter *writer = &context.xmlWriter(); 0159 writer->startElement("text:chapter ", false); 0160 switch(m_format) { 0161 case ChapterName: 0162 writer->addAttribute("text:display", "name"); 0163 break; 0164 case ChapterNumber: 0165 writer->addAttribute("text:display", "number"); 0166 break; 0167 case ChapterNumberName: 0168 writer->addAttribute("text:display", "number-and-name"); 0169 break; 0170 case ChapterPlainNumber: 0171 writer->addAttribute("text:display", "plain-number"); 0172 break; 0173 case ChapterPlainNumberName: 0174 writer->addAttribute("text:display", "plain-number-and-name"); 0175 break; 0176 default: 0177 break; 0178 } 0179 writer->addAttribute("text:outline-level", m_level); 0180 writer->addTextNode(value()); 0181 writer->endElement(); // text:chapter 0182 } 0183 0184 bool ChapterVariable::loadOdf(const KoXmlElement & element, KoShapeLoadingContext & context) 0185 { 0186 Q_UNUSED(context); 0187 0188 const QString display = element.attributeNS(KoXmlNS::text, "display", QString()); 0189 if (display == "name") { 0190 m_format = ChapterName; 0191 } else if (display == "number") { 0192 m_format = ChapterNumber; 0193 } else if (display == "number-and-name") { 0194 m_format = ChapterNumberName; 0195 } else if (display == "plain-number") { 0196 m_format = ChapterPlainNumber; 0197 } else if (display == "plain-number-and-name") { 0198 m_format = ChapterPlainNumberName; 0199 } else { // fallback 0200 m_format = ChapterNumberName; 0201 } 0202 0203 m_level = qMax(1, element.attributeNS(KoXmlNS::text, "outline-level", QString()).toInt()); 0204 0205 return true; 0206 } 0207 0208 QWidget* ChapterVariable::createOptionsWidget() 0209 { 0210 QWidget *widget = new QWidget(); 0211 QGridLayout *layout = new QGridLayout(widget); 0212 layout->setColumnStretch(1, 1); 0213 widget->setLayout(layout); 0214 0215 QLabel *formatLabel = new QLabel(i18n("Format:"), widget); 0216 formatLabel->setAlignment(Qt::AlignRight); 0217 layout->addWidget(formatLabel, 0, 0); 0218 QComboBox *formatEdit = new QComboBox(widget); 0219 formatLabel->setBuddy(formatEdit); 0220 formatEdit->addItems( QStringList() << i18n("Number") << i18n("Name") << i18n("Number and name") << i18n("Number without separator") << i18n("Number and name without separator") ); 0221 formatEdit->setCurrentIndex(2); 0222 layout->addWidget(formatEdit, 0, 1); 0223 0224 QLabel *levelLabel = new QLabel(i18n("Level:"), widget); 0225 levelLabel->setAlignment(Qt::AlignRight); 0226 layout->addWidget(levelLabel, 1, 0); 0227 QSpinBox *levelEdit = new QSpinBox(widget); 0228 levelLabel->setBuddy(levelEdit); 0229 levelEdit->setMinimum(1); 0230 levelEdit->setValue(m_level); 0231 layout->addWidget(levelEdit, 1, 1); 0232 0233 connect(formatEdit, SIGNAL(currentIndexChanged(int)), this, SLOT(formatChanged(int))); 0234 connect(levelEdit, SIGNAL(valueChanged(int)), this, SLOT(levelChanged(int))); 0235 0236 return widget; 0237 } 0238 0239 void ChapterVariable::formatChanged(int format) 0240 { 0241 m_format = (FormatTypes) format; 0242 } 0243 0244 void ChapterVariable::levelChanged(int level) 0245 { 0246 m_level = level; 0247 }