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 }