Warning, file /office/calligra/libs/textlayout/ListItemsHelper.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* This file is part of the KDE project 0002 * Copyright (C) 2006-2007, 2010 Thomas Zander <zander@kde.org> 0003 * Copyright (C) 2008 Girish Ramakrishnan <girish@forwardbias.in> 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 "ListItemsHelper.h" 0022 0023 #include <KoTextBlockData.h> 0024 #include <KoParagraphStyle.h> 0025 #include <KoTextDocument.h> 0026 #include <KoList.h> 0027 #include <KoOdfNumberDefinition.h> 0028 0029 #include <TextLayoutDebug.h> 0030 #include <klocalizedstring.h> 0031 #include <QTextList> 0032 0033 0034 // ------------------- ListItemsHelper ------------ 0035 /// \internal helper class for calculating text-lists prefixes and indents 0036 ListItemsHelper::ListItemsHelper(QTextList *textList, const QFont &font) 0037 : m_textList(textList) 0038 , m_fm(font, textList->document()->documentLayout()->paintDevice()) 0039 { 0040 } 0041 0042 void ListItemsHelper::recalculateBlock(QTextBlock &block) 0043 { 0044 //warnTextLayout; 0045 const QTextListFormat format = m_textList->format(); 0046 const KoListStyle::LabelType labelType = static_cast<KoListStyle::LabelType>(format.style()); 0047 0048 const QString prefix = format.stringProperty(KoListStyle::ListItemPrefix); 0049 const QString suffix = format.stringProperty(KoListStyle::ListItemSuffix); 0050 const int level = format.intProperty(KoListStyle::Level); 0051 int dp = format.intProperty(KoListStyle::DisplayLevel); 0052 if (dp > level) 0053 dp = level; 0054 const int displayLevel = dp ? dp : 1; 0055 0056 QTextBlockFormat blockFormat = block.blockFormat(); 0057 0058 // Look if we have a block that is inside a header. We need to special case them cause header-lists are 0059 // different from any other kind of list and they do build up there own global list (table of content). 0060 bool isOutline = blockFormat.intProperty(KoParagraphStyle::OutlineLevel) > 0; 0061 0062 int startValue = 1; 0063 if (format.hasProperty(KoListStyle::StartValue)) 0064 startValue = format.intProperty(KoListStyle::StartValue); 0065 0066 int index = startValue; 0067 bool fixed = false; 0068 if (blockFormat.boolProperty(KoParagraphStyle::RestartListNumbering)) { 0069 index = format.intProperty(KoListStyle::StartValue); 0070 fixed = true; 0071 } 0072 const int paragIndex = blockFormat.intProperty(KoParagraphStyle::ListStartValue); 0073 if (paragIndex > 0) { 0074 index = paragIndex; 0075 fixed = true; 0076 } 0077 0078 if (!fixed) { 0079 //if this is the first item then find if the list has to be continued from any other list 0080 KoList *listContinued = 0; 0081 if (m_textList->itemNumber(block) == 0 && KoTextDocument(m_textList->document()).list(m_textList) && (listContinued = KoTextDocument(m_textList->document()).list(m_textList)->listContinuedFrom())) { 0082 //find the previous list of the same level 0083 QTextList *previousTextList = listContinued->textLists().at(level - 1).data(); 0084 if (previousTextList) { 0085 QTextBlock textBlock = previousTextList->item(previousTextList->count() - 1); 0086 if (textBlock.isValid()) { 0087 index = KoTextBlockData(textBlock).counterIndex() + 1; //resume the previous list count 0088 } 0089 } 0090 } else if (m_textList->itemNumber(block) > 0) { 0091 QTextBlock textBlock = m_textList->item(m_textList->itemNumber(block) - 1); 0092 if (textBlock.isValid()) { 0093 index = KoTextBlockData(textBlock).counterIndex() + 1; //resume the previous list count 0094 } 0095 } 0096 } 0097 0098 qreal width = 0.0; 0099 KoTextBlockData blockData(block); 0100 0101 if (blockFormat.boolProperty(KoParagraphStyle::UnnumberedListItem) 0102 || blockFormat.boolProperty(KoParagraphStyle::IsListHeader)) { 0103 blockData.setCounterPlainText(QString()); 0104 blockData.setCounterPrefix(QString()); 0105 blockData.setCounterSuffix(QString()); 0106 blockData.setPartialCounterText(QString()); 0107 // set the counter for the current un-numbered list to the counter index of the previous list item. 0108 // index-1 because the list counter would have already incremented by one 0109 blockData.setCounterIndex(index - 1); 0110 if (blockFormat.boolProperty(KoParagraphStyle::IsListHeader)) { 0111 blockData.setCounterWidth(format.doubleProperty(KoListStyle::MinimumWidth)); 0112 blockData.setCounterSpacing(0); 0113 } 0114 return; 0115 } 0116 0117 QString item; 0118 if (displayLevel > 1) { 0119 int checkLevel = level; 0120 int tmpDisplayLevel = displayLevel; 0121 bool counterResetRequired = true; 0122 for (QTextBlock b = block.previous(); tmpDisplayLevel > 1 && b.isValid(); b = b.previous()) { 0123 if (b.textList() == 0) 0124 continue; 0125 QTextListFormat lf = b.textList()->format(); 0126 if (lf.property(KoListStyle::StyleId) != format.property(KoListStyle::StyleId)) 0127 continue; // uninteresting for us 0128 if (isOutline != bool(b.blockFormat().intProperty(KoParagraphStyle::OutlineLevel))) 0129 continue; // also uninteresting cause the one is an outline-listitem while the other is not 0130 0131 if (! KoListStyle::isNumberingStyle(static_cast<KoListStyle::LabelType>(lf.style()))) { 0132 continue; 0133 } 0134 0135 if (b.blockFormat().boolProperty(KoParagraphStyle::UnnumberedListItem)) { 0136 continue; //unnumbered listItems are irrelevant 0137 } 0138 0139 const int otherLevel = lf.intProperty(KoListStyle::Level); 0140 if (isOutline && checkLevel == otherLevel) { 0141 counterResetRequired = false; 0142 } 0143 0144 if (checkLevel <= otherLevel) 0145 continue; 0146 0147 KoTextBlockData otherData(b); 0148 if (!otherData.hasCounterData()) { 0149 continue; 0150 } 0151 if (tmpDisplayLevel - 1 < otherLevel) { // can't just copy it fully since we are 0152 // displaying less then the full counter 0153 item += otherData.partialCounterText(); 0154 tmpDisplayLevel--; 0155 checkLevel--; 0156 for (int i = otherLevel + 1; i < level; i++) { 0157 tmpDisplayLevel--; 0158 KoOdfNumberDefinition numberFormat; 0159 numberFormat.setFormatSpecification(static_cast<KoOdfNumberDefinition::FormatSpecification>(format.intProperty(KoListStyle::NumberFormat))); 0160 numberFormat.setLetterSynchronization(format.boolProperty(KoListStyle::LetterSynchronization)); 0161 item += "." + numberFormat.formattedNumber(index); // add missing counters. 0162 } 0163 } else { // just copy previous counter as prefix 0164 QString otherPrefix = lf.stringProperty(KoListStyle::ListItemPrefix); 0165 QString otherSuffix = lf.stringProperty(KoListStyle::ListItemSuffix); 0166 QString pureCounter = otherData.counterText().mid(otherPrefix.size()); 0167 pureCounter = pureCounter.left(pureCounter.size() - otherSuffix.size()); 0168 item += pureCounter; 0169 for (int i = otherLevel + 1; i < level; i++) { 0170 KoOdfNumberDefinition numberFormat; 0171 numberFormat.setFormatSpecification(static_cast<KoOdfNumberDefinition::FormatSpecification>(format.intProperty(KoListStyle::NumberFormat))); 0172 numberFormat.setLetterSynchronization(format.boolProperty(KoListStyle::LetterSynchronization)); 0173 item += "." + numberFormat.formattedNumber(index); // add missing counters. 0174 } 0175 tmpDisplayLevel = 0; 0176 if (isOutline && counterResetRequired) { 0177 index = 1; 0178 } 0179 break; 0180 } 0181 } 0182 for (int i = 1; i < tmpDisplayLevel; i++) { 0183 KoOdfNumberDefinition numberFormat; 0184 numberFormat.setFormatSpecification(static_cast<KoOdfNumberDefinition::FormatSpecification>(format.intProperty(KoListStyle::NumberFormat))); 0185 numberFormat.setLetterSynchronization(format.boolProperty(KoListStyle::LetterSynchronization)); 0186 item = numberFormat.formattedNumber(index) + "." + item; // add missing counters. 0187 } 0188 } 0189 bool calcWidth = true; 0190 QString partialCounterText; 0191 switch (labelType) { 0192 case KoListStyle::NumberLabelType: { 0193 KoOdfNumberDefinition::FormatSpecification spec = static_cast<KoOdfNumberDefinition::FormatSpecification>(format.intProperty(KoListStyle::NumberFormat)); 0194 0195 if (!(item.isEmpty() || item.endsWith('.') || item.endsWith(' '))) { 0196 if (spec == KoOdfNumberDefinition::Numeric || spec == KoOdfNumberDefinition::AlphabeticLowerCase || 0197 spec == KoOdfNumberDefinition::AlphabeticUpperCase || 0198 spec == KoOdfNumberDefinition::RomanLowerCase || 0199 spec == KoOdfNumberDefinition::RomanUpperCase) { 0200 item += '.'; 0201 } 0202 } 0203 0204 KoOdfNumberDefinition numberFormat; 0205 numberFormat.setFormatSpecification(spec); 0206 partialCounterText = numberFormat.formattedNumber(index); 0207 break; 0208 } 0209 case KoListStyle::BulletCharLabelType: { 0210 calcWidth = false; 0211 if (format.intProperty(KoListStyle::BulletCharacter)) 0212 item = QString(QChar(format.intProperty(KoListStyle::BulletCharacter))); 0213 width = m_fm.width(item); 0214 int percent = format.intProperty(KoListStyle::RelativeBulletSize); 0215 if (percent > 0) 0216 width = width * (percent / 100.0); 0217 break; 0218 } 0219 case KoListStyle::None: 0220 calcWidth = false; 0221 width = 0.0; 0222 break; 0223 case KoListStyle::ImageLabelType: 0224 calcWidth = false; 0225 width = qMax(format.doubleProperty(KoListStyle::Width), (qreal)1.0); 0226 break; 0227 default: // others we ignore. 0228 calcWidth = false; 0229 } 0230 0231 blockData.setCounterIsImage(labelType == KoListStyle::ImageLabelType); 0232 blockData.setPartialCounterText(partialCounterText); 0233 blockData.setCounterIndex(index); 0234 item += partialCounterText; 0235 blockData.setCounterPlainText(item); 0236 blockData.setCounterPrefix(prefix); 0237 blockData.setCounterSuffix(suffix); 0238 if (calcWidth) 0239 width = m_fm.width(item); 0240 index++; 0241 0242 width += m_fm.width(prefix + suffix); 0243 0244 qreal counterSpacing = 0; 0245 if (format.boolProperty(KoListStyle::AlignmentMode)) { 0246 // for aligmentmode spacing should be 0 0247 counterSpacing = 0; 0248 } else { 0249 if (labelType != KoListStyle::None) { 0250 // see ODF spec 1.2 item 20.422 0251 counterSpacing = format.doubleProperty(KoListStyle::MinimumDistance); 0252 if (width < format.doubleProperty(KoListStyle::MinimumWidth)) { 0253 counterSpacing -= format.doubleProperty(KoListStyle::MinimumWidth) - width; 0254 } 0255 counterSpacing = qMax(counterSpacing, qreal(0.0)); 0256 } 0257 width = qMax(width, format.doubleProperty(KoListStyle::MinimumWidth)); 0258 } 0259 blockData.setCounterWidth(width); 0260 blockData.setCounterSpacing(counterSpacing); 0261 } 0262 0263 // static 0264 bool ListItemsHelper::needsRecalc(QTextList *textList) 0265 { 0266 Q_ASSERT(textList); 0267 QTextBlock tb = textList->item(0); 0268 KoTextBlockData blockData(tb); 0269 return !blockData.hasCounterData(); 0270 }