File indexing completed on 2024-05-26 16:15:51

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2008 Girish Ramakrishnan <girish@forwardbias.in>
0003  * Copyright (C) 2010 Thomas Zander <zander@kde.org>
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Library General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Library General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Library General Public License
0016  * along with this library; see the file COPYING.LIB.  If not, write to
0017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019  */
0020 
0021 #include "KoList.h"
0022 #include "KoList_p.h"
0023 
0024 #include "KoTextDocument.h"
0025 #include "styles/KoListLevelProperties.h"
0026 #include "styles/KoParagraphStyle.h"
0027 #include "styles/KoStyleManager.h"
0028 
0029 #include "TextDebug.h"
0030 
0031 #include <QTextCursor>
0032 
0033 KoList::KoList(const QTextDocument *document, KoListStyle *style, KoList::Type type)
0034     : QObject(const_cast<QTextDocument *>(document)), d(new KoListPrivate(this, document))
0035 {
0036     Q_ASSERT(document);
0037     d->type = type;
0038     setStyle(style);
0039     KoTextDocument(document).addList(this);
0040 }
0041 
0042 KoList::~KoList()
0043 {
0044     KoTextDocument(d->document).removeList(this);
0045     delete d;
0046 }
0047 
0048 QVector<QPointer<QTextList> > KoList::textLists() const
0049 {
0050     return d->textLists;
0051 }
0052 
0053 QVector<KoListStyle::ListIdType> KoList::textListIds() const
0054 {
0055     return d->textListIds;
0056 }
0057 
0058 KoList *KoList::applyStyle(const QTextBlock &block, KoListStyle *style, int level)
0059 {
0060     Q_ASSERT(style);
0061     KoTextDocument document(block.document());
0062     KoList *list = document.list(block);
0063     if (list && *list->style() == *style) {
0064         list->add(block, level);
0065         return list;
0066     }
0067 
0068     //the block was already another list but with a different style - remove block from list
0069     if (list)
0070         list->remove(block);
0071 
0072     // Ok, so we are now ready to add the block to another list, but which other list?
0073     // For headers we always want to continue from any previous header
0074     // For normal lists we either want to continue an adjacent list or create a new one
0075     if (block.blockFormat().hasProperty(KoParagraphStyle::OutlineLevel)) {
0076         for (QTextBlock b = block.previous();b.isValid(); b = b.previous()) {
0077             list = document.list(b);
0078             if (list && *list->style() == *style) {
0079                 break;
0080             }
0081         }
0082         if (!list || *list->style() != *style) {
0083             list = new KoList(block.document(), style);
0084         }
0085     } else {
0086         list = document.list(block.previous());
0087         if (!list || *list->style() != *style) {
0088             list = document.list(block.next());
0089             if (!list || *list->style() != *style) {
0090                 list = new KoList(block.document(), style);
0091             }
0092         }
0093     }
0094     list->add(block, level);
0095     return list;
0096 }
0097 
0098 void KoList::add(const QTextBlock &block, int level)
0099 {
0100     if (!block.isValid())
0101         return;
0102 
0103     Q_ASSERT(level >= 0 && level <= 10);
0104     if (level == 0) { // fetch the first proper level we have
0105         level = 1; // if nothing works...
0106         for (int i = 1; i <= 10; i++) {
0107             if (d->style->hasLevelProperties(i)) {
0108                 level = i;
0109                 break;
0110             }
0111         }
0112     }
0113     remove(block);
0114 
0115     QTextList *textList = d->textLists.value(level-1).data();
0116     if (!textList) {
0117         QTextCursor cursor(block);
0118         QTextListFormat format = d->style->listFormat(level);
0119         textList = cursor.createList(format);
0120         format.setProperty(KoListStyle::ListId, (KoListStyle::ListIdType)(textList));
0121         textList->setFormat(format);
0122         d->textLists[level-1] = textList;
0123         d->textListIds[level-1] = (KoListStyle::ListIdType)textList;
0124     } else {
0125         textList->add(block);
0126     }
0127 
0128     QTextCursor cursor(block);
0129     QTextBlockFormat blockFormat = cursor.blockFormat();
0130     if (d->style->styleId()) {
0131         blockFormat.setProperty(KoParagraphStyle::ListStyleId, d->style->styleId());
0132     } else {
0133         blockFormat.clearProperty(KoParagraphStyle::ListStyleId);
0134     }
0135     if (d->type == KoList::TextList) {
0136         blockFormat.clearProperty(KoParagraphStyle::ListLevel);
0137     } else {
0138         blockFormat.setProperty(KoParagraphStyle::ListLevel, level);
0139     }
0140     cursor.setBlockFormat(blockFormat);
0141 
0142     d->invalidate(block);
0143 }
0144 
0145 void KoList::remove(const QTextBlock &block)
0146 {
0147     //QTextLists are created with a blockIndent of 1. When a block is removed from a QTextList, it's blockIndent is set to (block.indent + list.indent).
0148     //Since we do not use Qt's indentation for lists, we need to clear the block's blockIndent, otherwise the block's style will appear as modified.
0149     bool clearIndent = !block.blockFormat().hasProperty(4160);
0150 
0151     if (QTextList *textList = block.textList()) {
0152         // invalidate the list before we remove the item
0153         // (since the list might disappear if the block is the only item)
0154         KoListPrivate::invalidateList(block);
0155         textList->remove(block);
0156     }
0157     KoListPrivate::invalidate(block);
0158 
0159     if (clearIndent) {
0160         QTextBlockFormat format = block.blockFormat();
0161         format.clearProperty(4160);
0162         QTextCursor cursor(block);
0163         cursor.setBlockFormat(format);
0164     }
0165 }
0166 
0167 void KoList::setStyle(KoListStyle *style)
0168 {
0169     if (style == 0) {
0170         KoStyleManager *styleManager = KoTextDocument(d->document).styleManager();
0171         Q_ASSERT(styleManager);
0172         style = styleManager->defaultListStyle();
0173     }
0174 
0175     if (style != d->style) {
0176         if (d->style)
0177             disconnect(d->style, 0, this, 0);
0178         d->style = style->clone(this);
0179         connect(d->style, SIGNAL(styleChanged(int)), this, SLOT(styleChanged(int)));
0180     }
0181 
0182     for (int i = 0; i < d->textLists.count(); i++) {
0183         QTextList *textList = d->textLists.value(i).data();
0184         if (!textList)
0185             continue;
0186         KoListLevelProperties properties = d->style->levelProperties(i+1);
0187         if (properties.listId())
0188             d->textListIds[i] = properties.listId();
0189         QTextListFormat format;
0190         properties.applyStyle(format);
0191         textList->setFormat(format);
0192         d->invalidate(textList->item(0));
0193     }
0194 
0195     //if this list is a heading list then update the style manager with the list properties
0196     if (KoTextDocument(d->document).headingList() == this) {
0197         if (KoStyleManager *styleManager = KoTextDocument(d->document).styleManager()) {
0198             if (styleManager->outlineStyle()) {
0199                 styleManager->outlineStyle()->copyProperties(style);
0200             }
0201         }
0202     }
0203 }
0204 
0205 KoListStyle *KoList::style() const
0206 {
0207     return d->style;
0208 }
0209 
0210 void KoList::updateStoredList(const QTextBlock &block)
0211 {
0212     if (block.textList()) {
0213         int level = block.textList()->format().property(KoListStyle::Level).toInt();
0214         QTextList *textList = block.textList();
0215         textList->format().setProperty(KoListStyle::ListId, (KoListStyle::ListIdType)(textList));
0216         d->textLists[level-1] = textList;
0217         d->textListIds[level-1] = (KoListStyle::ListIdType)textList;
0218     }
0219 }
0220 
0221 bool KoList::contains(QTextList *list) const
0222 {
0223     return list && d->textLists.contains(list);
0224 }
0225 
0226 int KoList::level(const QTextBlock &block)
0227 {
0228     if (!block.textList())
0229         return 0;
0230     int l = block.blockFormat().intProperty(KoParagraphStyle::ListLevel);
0231     if (!l) { // not a numbered-paragraph
0232         QTextListFormat format = block.textList()->format();
0233         l = format.intProperty(KoListStyle::Level);
0234     }
0235     return l;
0236 }
0237 
0238 KoList *KoList::listContinuedFrom() const
0239 {
0240     return d->listToBeContinuedFrom;
0241 }
0242 
0243 void KoList::setListContinuedFrom(KoList *list)
0244 {
0245     d->listToBeContinuedFrom = list;
0246 }
0247 
0248 //have to include this because of Q_PRIVATE_SLOT
0249 #include "moc_KoList.cpp"