You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
988 lines
33 KiB
988 lines
33 KiB
/* This file is part of the KDE project
|
|
Copyright (C) 2001 Shaheed Haque <srhaque@iee.org>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "KoParagCounter.h"
|
|
#include "KoTextParag.h"
|
|
#include "KoTextZoomHandler.h"
|
|
#include "KoTextFormat.h"
|
|
#include "KoTextDocument.h"
|
|
#include "KoOasisContext.h"
|
|
#include <KoXmlWriter.h>
|
|
#include <KoGenStyles.h>
|
|
#include <KoXmlNS.h>
|
|
#include <kdebug.h>
|
|
#include <tqdom.h>
|
|
#include <tqbuffer.h>
|
|
|
|
static KoTextParag * const INVALID_PARAG = (KoTextParag *)-1;
|
|
|
|
KoParagCounter::KoParagCounter()
|
|
{
|
|
m_numbering = NUM_NONE;
|
|
m_style = STYLE_NONE;
|
|
m_depth = 0;
|
|
m_startNumber = 1;
|
|
m_displayLevels = 1;
|
|
m_restartCounter = false;
|
|
m_customBulletChar = TQChar( '-' );
|
|
m_customBulletFont = TQString();
|
|
m_align = TQt::AlignAuto;
|
|
tqinvalidate();
|
|
}
|
|
|
|
bool KoParagCounter::operator==( const KoParagCounter & c2 ) const
|
|
{
|
|
// ## This is kinda wrong. Unused fields (depending on the counter style) shouldn't be compared.
|
|
return (m_numbering==c2.m_numbering &&
|
|
m_style==c2.m_style &&
|
|
m_depth==c2.m_depth &&
|
|
m_startNumber==c2.m_startNumber &&
|
|
m_displayLevels==c2.m_displayLevels &&
|
|
m_restartCounter==c2.m_restartCounter &&
|
|
m_prefix==c2.m_prefix &&
|
|
m_suffix==c2.m_suffix &&
|
|
m_customBulletChar==c2.m_customBulletChar &&
|
|
m_customBulletFont==c2.m_customBulletFont &&
|
|
m_align==c2.m_align &&
|
|
m_custom==c2.m_custom);
|
|
}
|
|
|
|
TQString KoParagCounter::custom() const
|
|
{
|
|
return m_custom;
|
|
}
|
|
|
|
TQChar KoParagCounter::customBulletCharacter() const
|
|
{
|
|
return m_customBulletChar;
|
|
}
|
|
|
|
TQString KoParagCounter::customBulletFont() const
|
|
{
|
|
return m_customBulletFont;
|
|
}
|
|
|
|
unsigned int KoParagCounter::depth() const
|
|
{
|
|
return m_depth;
|
|
}
|
|
|
|
void KoParagCounter::tqinvalidate()
|
|
{
|
|
m_cache.number = -1;
|
|
m_cache.text = TQString();
|
|
m_cache.width = -1;
|
|
m_cache.tqparent = INVALID_PARAG;
|
|
m_cache.counterFormat = 0;
|
|
}
|
|
|
|
bool KoParagCounter::isBullet( Style style ) // static
|
|
{
|
|
switch ( style )
|
|
{
|
|
case STYLE_DISCBULLET:
|
|
case STYLE_SQUAREBULLET:
|
|
case STYLE_BOXBULLET:
|
|
case STYLE_CIRCLEBULLET:
|
|
case STYLE_CUSTOMBULLET:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool KoParagCounter::isBullet() const
|
|
{
|
|
return isBullet( m_style );
|
|
}
|
|
|
|
void KoParagCounter::load( TQDomElement & element )
|
|
{
|
|
m_numbering = static_cast<Numbering>( element.attribute("numberingtype", "2").toInt() );
|
|
m_style = static_cast<Style>( element.attribute("type").toInt() );
|
|
// Old docs have this:
|
|
if ( m_numbering == NUM_LIST && m_style == STYLE_NONE )
|
|
m_numbering = NUM_NONE;
|
|
m_depth = element.attribute("depth").toInt();
|
|
m_customBulletChar = TQChar( element.attribute("bullet").toInt() );
|
|
m_prefix = element.attribute("lefttext");
|
|
if ( m_prefix.lower() == "(null)" ) // very old kword thing
|
|
m_prefix = TQString();
|
|
m_suffix = element.attribute("righttext");
|
|
if ( m_suffix.lower() == "(null)" )
|
|
m_suffix = TQString();
|
|
TQString s = element.attribute("start");
|
|
if ( s.isEmpty() )
|
|
m_startNumber = 1;
|
|
else if ( s[0].isDigit() )
|
|
m_startNumber = s.toInt();
|
|
else // support for very-old files
|
|
m_startNumber = s.lower()[0].latin1() - 'a' + 1;
|
|
s = element.attribute("display-levels");
|
|
if ( !s.isEmpty() )
|
|
m_displayLevels = TQMIN( s.toInt(), m_depth+1 ); // can't be > depth+1
|
|
else // Not specified -> compat with koffice-1.2: make equal to depth+1
|
|
m_displayLevels = m_depth+1;
|
|
m_customBulletFont = element.attribute("bulletfont");
|
|
m_custom = element.attribute("customdef");
|
|
m_align = element.attribute("align", "0").toInt(); //AlignAuto as defeult
|
|
TQString restart = element.attribute("restart");
|
|
m_restartCounter = (restart == "true") || (restart == "1");
|
|
tqinvalidate();
|
|
}
|
|
|
|
static int importCounterType( TQChar numFormat )
|
|
{
|
|
if ( numFormat == '1' )
|
|
return KoParagCounter::STYLE_NUM;
|
|
if ( numFormat == 'a' )
|
|
return KoParagCounter::STYLE_ALPHAB_L;
|
|
if ( numFormat == 'A' )
|
|
return KoParagCounter::STYLE_ALPHAB_U;
|
|
if ( numFormat == 'i' )
|
|
return KoParagCounter::STYLE_ROM_NUM_L;
|
|
if ( numFormat == 'I' )
|
|
return KoParagCounter::STYLE_ROM_NUM_U;
|
|
return KoParagCounter::STYLE_NONE;
|
|
}
|
|
|
|
// should only be called with style != none and != a bullet.
|
|
static TQChar exportCounterType( KoParagCounter::Style style )
|
|
{
|
|
static const int s_oasisCounterTypes[] =
|
|
{ '\0', '1', 'a', 'A', 'i', 'I',
|
|
'\0', '\0', // custombullet, custom
|
|
0x2022, // circle -> small disc
|
|
0xE00A, // square
|
|
0x25CF, // disc -> large disc
|
|
0x27A2 // box -> right-pointing triangle
|
|
};
|
|
return TQChar( s_oasisCounterTypes[ style ] );
|
|
}
|
|
|
|
void KoParagCounter::loadOasis( KoOasisContext& context, int restartNumbering,
|
|
bool orderedList, bool heading, int level, bool loadingStyle )
|
|
{
|
|
const TQDomElement listStyle = context.listStyleStack().currentListStyle();
|
|
const TQDomElement listStyleProperties = context.listStyleStack().currentListStyleProperties();
|
|
const TQDomElement listStyleTextProperties = context.listStyleStack().currentListStyleTextProperties();
|
|
loadOasisListStyle( listStyle, listStyleProperties, listStyleTextProperties,
|
|
restartNumbering, orderedList, heading, level, loadingStyle );
|
|
}
|
|
|
|
void KoParagCounter::loadOasisListStyle( const TQDomElement& listStyle,
|
|
const TQDomElement& listStyleProperties,
|
|
const TQDomElement& listStyleTextProperties,
|
|
int restartNumbering,
|
|
bool orderedList, bool heading, int level,
|
|
bool loadingStyle )
|
|
{
|
|
m_numbering = heading ? NUM_CHAPTER : NUM_LIST;
|
|
m_depth = level - 1; // depth start at 0
|
|
// restartNumbering can either be provided by caller, or taken from the style
|
|
if ( restartNumbering == -1 && listStyle.hasAttributeNS( KoXmlNS::text, "start-value" ) )
|
|
restartNumbering = listStyle.attributeNS( KoXmlNS::text, "start-value", TQString() ).toInt();
|
|
|
|
// styles have a start-value, but that doesn't mean restartNumbering, as it does for paragraphs
|
|
m_restartCounter = loadingStyle ? false : ( restartNumbering != -1 );
|
|
m_startNumber = ( restartNumbering != -1 ) ? restartNumbering : 1;
|
|
//kdDebug() << k_funcinfo << "IN: restartNumbering=" << restartNumbering << " OUT: m_restartCounter=" << m_restartCounter << " m_startNumber=" << m_startNumber << endl;
|
|
|
|
m_prefix = listStyle.attributeNS( KoXmlNS::style, "num-prefix", TQString() );
|
|
m_suffix = listStyle.attributeNS( KoXmlNS::style, "num-suffix", TQString() );
|
|
|
|
if ( orderedList || heading ) {
|
|
m_style = static_cast<Style>( importCounterType( listStyle.attributeNS( KoXmlNS::style, "num-format", TQString())[0] ) );
|
|
TQString dl = listStyle.attributeNS( KoXmlNS::text, "display-levels", TQString() );
|
|
m_displayLevels = dl.isEmpty() ? 1 : dl.toInt();
|
|
} else { // bullets, see 3.3.6 p138
|
|
m_style = STYLE_CUSTOMBULLET;
|
|
TQString bulletChar = listStyle.attributeNS( KoXmlNS::text, "bullet-char", TQString() );
|
|
if ( !bulletChar.isEmpty() ) {
|
|
// Reverse engineering, I found those codes:
|
|
switch( bulletChar[0].tqunicode() ) {
|
|
case 0x2022: // small disc -> circle
|
|
m_style = STYLE_CIRCLEBULLET;
|
|
break;
|
|
case 0x25CF: // large disc -> disc
|
|
case 0xF0B7: // #113361
|
|
m_style = STYLE_DISCBULLET;
|
|
break;
|
|
case 0xE00C: // losange - TODO in kotext. Not in OASIS either (reserved Unicode area!)
|
|
m_style = STYLE_BOXBULLET;
|
|
break;
|
|
case 0xE00A: // square. Not in OASIS (reserved Unicode area!), but used in both OOo and kotext.
|
|
m_style = STYLE_SQUAREBULLET;
|
|
break;
|
|
case 0x27A2: // two-colors right-pointing triangle
|
|
// mapping (both ways) to box for now.
|
|
m_style = STYLE_BOXBULLET;
|
|
break;
|
|
default:
|
|
kdDebug() << "Unhandled bullet code 0x" << TQString::number( (uint)m_customBulletChar.tqunicode(), 16 ) << endl;
|
|
// fallback
|
|
case 0x2794: // arrow
|
|
case 0x2717: // cross
|
|
case 0x2714: // checkmark
|
|
m_customBulletChar = bulletChar[0];
|
|
// often StarSymbol when it comes from OO; doesn't matter, TQt finds it in another font if needed.
|
|
if ( listStyleProperties.hasAttributeNS( KoXmlNS::style, "font-name" ) )
|
|
{
|
|
m_customBulletFont = listStyleProperties.attributeNS( KoXmlNS::style, "font-name", TQString() );
|
|
kdDebug() << "m_customBulletFont style:font-name = " << listStyleProperties.attributeNS( KoXmlNS::style, "font-name", TQString() ) << endl;
|
|
}
|
|
else if ( listStyleTextProperties.hasAttributeNS( KoXmlNS::fo, "font-family" ) )
|
|
{
|
|
m_customBulletFont = listStyleTextProperties.attributeNS( KoXmlNS::fo, "font-family", TQString() );
|
|
kdDebug() << "m_customBulletFont fo:font-family = " << listStyleTextProperties.attributeNS( KoXmlNS::fo, "font-family", TQString() ) << endl;
|
|
}
|
|
// ## TODO in fact we're supposed to read it from the style pointed to by text:style-name
|
|
break;
|
|
}
|
|
} else { // can never happen
|
|
m_style = STYLE_DISCBULLET;
|
|
}
|
|
}
|
|
tqinvalidate();
|
|
}
|
|
|
|
void KoParagCounter::saveOasis( KoGenStyle& listStyle, bool savingStyle ) const
|
|
{
|
|
Q_ASSERT( (Style)m_style != STYLE_NONE );
|
|
|
|
// Prepare a sub-xmlwriter for the list-level-style-* element
|
|
TQBuffer buffer;
|
|
buffer.open( IO_WriteOnly );
|
|
KoXmlWriter listLevelWriter( TQT_TQIODEVICE(&buffer), 3 /*indentation*/ );
|
|
const char* tagName = isBullet() ? "text:list-level-style-bullet" : "text:list-level-style-number";
|
|
listLevelWriter.startElement( tagName );
|
|
|
|
saveOasisListLevel( listLevelWriter, true, savingStyle );
|
|
|
|
listLevelWriter.endElement();
|
|
const TQString listLevelContents = TQString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
|
|
listStyle.addChildElement( tagName, listLevelContents );
|
|
}
|
|
|
|
void KoParagCounter::saveOasisListLevel( KoXmlWriter& listLevelWriter, bool includeLevelAndProperties, bool savingStyle ) const
|
|
{
|
|
if ( includeLevelAndProperties ) // false when called for footnotes-configuration
|
|
listLevelWriter.addAttribute( "text:level", (int)m_depth + 1 );
|
|
// OASIS allows to specify a text:style, the character style to use for the numbering...
|
|
// We currently always format as per the first character of the paragraph, but that's not perfect.
|
|
|
|
if ( isBullet() )
|
|
{
|
|
TQChar bulletChar;
|
|
if ( (Style)m_style == STYLE_CUSTOMBULLET )
|
|
{
|
|
bulletChar = m_customBulletChar;
|
|
// TODO font (text style)
|
|
}
|
|
else
|
|
{
|
|
bulletChar = exportCounterType( (Style)m_style );
|
|
}
|
|
listLevelWriter.addAttribute( "text:bullet-char", TQString( bulletChar ) );
|
|
}
|
|
else
|
|
{
|
|
if ( includeLevelAndProperties ) // not for KWVariableSettings
|
|
listLevelWriter.addAttribute( "text:display-levels", m_displayLevels );
|
|
if ( (Style)m_style == STYLE_CUSTOM )
|
|
; // not implemented
|
|
else
|
|
listLevelWriter.addAttribute( "style:num-format", TQString( exportCounterType( (Style)m_style ) ) );
|
|
|
|
// m_startNumber/m_restartCounter is saved by kotextparag itself, except for styles.
|
|
if ( savingStyle && m_restartCounter ) {
|
|
listLevelWriter.addAttribute( "text:start-value", m_startNumber );
|
|
}
|
|
|
|
}
|
|
// m_numbering isn't saved, it's set depending on context (NUM_CHAPTER for headings).
|
|
|
|
listLevelWriter.addAttribute( "style:num-prefix", m_prefix );
|
|
listLevelWriter.addAttribute( "style:num-suffix", m_suffix );
|
|
|
|
if ( includeLevelAndProperties ) // false when called for footnotes-configuration
|
|
{
|
|
listLevelWriter.startElement( "style:list-level-properties" );
|
|
listLevelWriter.addAttribute( "fo:text-align", KoParagLayout::saveOasisAlignment( (TQt::AlignmentFlags)m_align ) );
|
|
// OASIS has other style properties: text:space-before (indent), text:min-label-width (TODO),
|
|
// text:min-label-distance, style:font-name (for bullets), image size and vertical tqalignment.
|
|
listLevelWriter.endElement(); // style:list-level-properties
|
|
}
|
|
}
|
|
|
|
int KoParagCounter::number( const KoTextParag *paragraph )
|
|
{
|
|
// Return cached value if possible.
|
|
if ( m_cache.number != -1 )
|
|
return m_cache.number;
|
|
|
|
// Should we start a new list?
|
|
if ( m_restartCounter ) {
|
|
Q_ASSERT( m_startNumber != -1 );
|
|
m_cache.number = m_startNumber;
|
|
return m_startNumber;
|
|
}
|
|
|
|
// Go looking for another paragraph at the same level or higher level.
|
|
// (This code shares logic with tqparent())
|
|
KoTextParag *otherParagraph = paragraph->prev();
|
|
KoParagCounter *otherCounter;
|
|
|
|
switch ( m_numbering )
|
|
{
|
|
case NUM_NONE:
|
|
// This should not occur!
|
|
case NUM_FOOTNOTE:
|
|
m_cache.number = 0;
|
|
break;
|
|
case NUM_CHAPTER:
|
|
m_cache.number = m_startNumber;
|
|
// Go upwards...
|
|
while ( otherParagraph )
|
|
{
|
|
otherCounter = otherParagraph->counter();
|
|
if ( otherCounter && // ...look at numbered paragraphs only
|
|
( otherCounter->m_numbering == NUM_CHAPTER ) && // ...same number type.
|
|
( otherCounter->m_depth <= m_depth ) ) // ...same or higher level.
|
|
{
|
|
if ( ( otherCounter->m_depth == m_depth ) &&
|
|
( otherCounter->m_style == m_style ) )
|
|
{
|
|
// Found a preceding paragraph of exactly our type!
|
|
m_cache.number = otherCounter->number( otherParagraph ) + 1;
|
|
}
|
|
else
|
|
{
|
|
// Found a preceding paragraph of higher level!
|
|
m_cache.number = m_startNumber;
|
|
}
|
|
break;
|
|
}
|
|
otherParagraph = otherParagraph->prev();
|
|
}
|
|
break;
|
|
case NUM_LIST:
|
|
m_cache.number = m_startNumber;
|
|
// Go upwards...
|
|
while ( otherParagraph )
|
|
{
|
|
otherCounter = otherParagraph->counter();
|
|
if ( otherCounter ) // look at numbered paragraphs only
|
|
{
|
|
if ( ( otherCounter->m_numbering == NUM_LIST ) && // ...same number type.
|
|
!isBullet( otherCounter->m_style ) && // ...not a bullet
|
|
( otherCounter->m_depth <= m_depth ) ) // ...same or higher level.
|
|
{
|
|
if ( ( otherCounter->m_depth == m_depth ) &&
|
|
( otherCounter->m_style == m_style ) )
|
|
{
|
|
// Found a preceding paragraph of exactly our type!
|
|
m_cache.number = otherCounter->number( otherParagraph ) + 1;
|
|
}
|
|
else
|
|
{
|
|
// Found a preceding paragraph of higher level!
|
|
m_cache.number = m_startNumber;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
if ( otherCounter->m_numbering == NUM_CHAPTER ) // ...heading number type.
|
|
{
|
|
m_cache.number = m_startNumber;
|
|
break;
|
|
}
|
|
}
|
|
/* else
|
|
{
|
|
// There is no counter at all.
|
|
m_cache.number = m_startNumber;
|
|
break;
|
|
}*/
|
|
otherParagraph = otherParagraph->prev();
|
|
}
|
|
break;
|
|
}
|
|
Q_ASSERT( m_cache.number != -1 );
|
|
return m_cache.number;
|
|
}
|
|
|
|
KoParagCounter::Numbering KoParagCounter::numbering() const
|
|
{
|
|
return m_numbering;
|
|
}
|
|
|
|
// Go looking for another paragraph at a higher level.
|
|
KoTextParag *KoParagCounter::tqparent( const KoTextParag *paragraph )
|
|
{
|
|
// Return cached value if possible.
|
|
if ( m_cache.tqparent != INVALID_PARAG )
|
|
return m_cache.tqparent;
|
|
|
|
KoTextParag *otherParagraph = paragraph->prev();
|
|
KoParagCounter *otherCounter;
|
|
|
|
// (This code shares logic with number())
|
|
switch ( m_numbering )
|
|
{
|
|
case NUM_NONE:
|
|
// This should not occur!
|
|
case NUM_FOOTNOTE:
|
|
otherParagraph = 0L;
|
|
break;
|
|
case NUM_CHAPTER:
|
|
// Go upwards while...
|
|
while ( otherParagraph )
|
|
{
|
|
otherCounter = otherParagraph->counter();
|
|
if ( otherCounter && // ...numbered paragraphs.
|
|
( otherCounter->m_numbering == NUM_CHAPTER ) && // ...same number type.
|
|
( otherCounter->m_depth < m_depth ) ) // ...higher level.
|
|
{
|
|
break;
|
|
}
|
|
otherParagraph = otherParagraph->prev();
|
|
}
|
|
break;
|
|
case NUM_LIST:
|
|
// Go upwards while...
|
|
while ( otherParagraph )
|
|
{
|
|
otherCounter = otherParagraph->counter();
|
|
if ( otherCounter ) // ...numbered paragraphs.
|
|
{
|
|
if ( ( otherCounter->m_numbering == NUM_LIST ) && // ...same number type.
|
|
!isBullet( otherCounter->m_style ) && // ...not a bullet
|
|
( otherCounter->m_depth < m_depth ) ) // ...higher level.
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
if ( otherCounter->m_numbering == NUM_CHAPTER ) // ...heading number type.
|
|
{
|
|
otherParagraph = 0L;
|
|
break;
|
|
}
|
|
}
|
|
otherParagraph = otherParagraph->prev();
|
|
}
|
|
break;
|
|
}
|
|
m_cache.tqparent = otherParagraph;
|
|
return m_cache.tqparent;
|
|
}
|
|
|
|
TQString KoParagCounter::prefix() const
|
|
{
|
|
return m_prefix;
|
|
}
|
|
|
|
void KoParagCounter::save( TQDomElement & element )
|
|
{
|
|
element.setAttribute( "type", static_cast<int>( m_style ) );
|
|
element.setAttribute( "depth", m_depth );
|
|
if ( (Style)m_style == STYLE_CUSTOMBULLET )
|
|
{
|
|
element.setAttribute( "bullet", m_customBulletChar.tqunicode() );
|
|
if ( !m_customBulletFont.isEmpty() )
|
|
element.setAttribute( "bulletfont", m_customBulletFont );
|
|
}
|
|
if ( !m_prefix.isEmpty() )
|
|
element.setAttribute( "lefttext", m_prefix );
|
|
if ( !m_suffix.isEmpty() )
|
|
element.setAttribute( "righttext", m_suffix );
|
|
if ( m_startNumber != 1 )
|
|
element.setAttribute( "start", m_startNumber );
|
|
//if ( m_displayLevels != m_depth ) // see load()
|
|
element.setAttribute( "display-levels", m_displayLevels );
|
|
// Don't need to save NUM_FOOTNOTE, it's updated right after loading
|
|
if ( m_numbering != NUM_NONE && m_numbering != NUM_FOOTNOTE )
|
|
element.setAttribute( "numberingtype", static_cast<int>( m_numbering ) );
|
|
if ( !m_custom.isEmpty() )
|
|
element.setAttribute( "customdef", m_custom );
|
|
if ( m_restartCounter )
|
|
element.setAttribute( "restart", "true" );
|
|
if ( !m_cache.text.isEmpty() )
|
|
element.setAttribute( "text", m_cache.text );
|
|
element.setAttribute( "align", m_align );
|
|
}
|
|
|
|
void KoParagCounter::setCustom( TQString c )
|
|
{
|
|
m_custom = c;
|
|
tqinvalidate();
|
|
}
|
|
|
|
void KoParagCounter::setCustomBulletCharacter( TQChar c )
|
|
{
|
|
m_customBulletChar = c;
|
|
tqinvalidate();
|
|
}
|
|
|
|
void KoParagCounter::setCustomBulletFont( TQString f )
|
|
{
|
|
m_customBulletFont = f;
|
|
tqinvalidate();
|
|
}
|
|
|
|
void KoParagCounter::setDepth( unsigned int d )
|
|
{
|
|
m_depth = d;
|
|
tqinvalidate();
|
|
}
|
|
|
|
void KoParagCounter::setNumbering( Numbering n )
|
|
{
|
|
m_numbering = n;
|
|
tqinvalidate();
|
|
}
|
|
|
|
void KoParagCounter::setPrefix( TQString p )
|
|
{
|
|
m_prefix = p;
|
|
tqinvalidate();
|
|
}
|
|
void KoParagCounter::setStartNumber( int s )
|
|
{
|
|
m_startNumber = s;
|
|
tqinvalidate();
|
|
}
|
|
|
|
void KoParagCounter::setDisplayLevels( int l )
|
|
{
|
|
m_displayLevels = l;
|
|
tqinvalidate();
|
|
}
|
|
|
|
void KoParagCounter::tqsetAlignment( int a )
|
|
{
|
|
m_align = a;
|
|
tqinvalidate();
|
|
}
|
|
|
|
void KoParagCounter::setStyle( Style s )
|
|
{
|
|
m_style = s;
|
|
tqinvalidate();
|
|
}
|
|
|
|
void KoParagCounter::setSuffix( TQString s )
|
|
{
|
|
m_suffix = s;
|
|
tqinvalidate();
|
|
}
|
|
|
|
int KoParagCounter::startNumber() const
|
|
{
|
|
return m_startNumber;
|
|
}
|
|
|
|
int KoParagCounter::displayLevels() const
|
|
{
|
|
return m_displayLevels;
|
|
}
|
|
|
|
int KoParagCounter::tqalignment() const
|
|
{
|
|
return m_align;
|
|
}
|
|
|
|
KoParagCounter::Style KoParagCounter::style() const
|
|
{
|
|
return m_style;
|
|
}
|
|
|
|
TQString KoParagCounter::suffix() const
|
|
{
|
|
return m_suffix;
|
|
}
|
|
|
|
bool KoParagCounter::restartCounter() const
|
|
{
|
|
return m_restartCounter;
|
|
}
|
|
|
|
void KoParagCounter::setRestartCounter( bool restart )
|
|
{
|
|
m_restartCounter = restart;
|
|
tqinvalidate();
|
|
}
|
|
|
|
// Return the text for that level only
|
|
TQString KoParagCounter::levelText( const KoTextParag *paragraph )
|
|
{
|
|
if ( m_numbering == NUM_NONE )
|
|
return "";
|
|
|
|
bool bullet = isBullet( m_style );
|
|
|
|
if ( bullet && m_numbering == NUM_CHAPTER ) {
|
|
// Shome mishtake surely! (not sure how it can happen though)
|
|
m_style = STYLE_NUM;
|
|
bullet = false;
|
|
}
|
|
|
|
TQString text;
|
|
if ( !bullet )
|
|
{
|
|
// Ensure paragraph number is valid.
|
|
number( paragraph );
|
|
|
|
switch ( m_style )
|
|
{
|
|
case STYLE_NONE:
|
|
if ( m_numbering == NUM_LIST )
|
|
text = ' ';
|
|
break;
|
|
case STYLE_NUM:
|
|
text.setNum( m_cache.number );
|
|
break;
|
|
case STYLE_ALPHAB_L:
|
|
text = makeAlphaLowerNumber( m_cache.number );
|
|
break;
|
|
case STYLE_ALPHAB_U:
|
|
text = makeAlphaUpperNumber( m_cache.number );
|
|
break;
|
|
case STYLE_ROM_NUM_L:
|
|
text = makeRomanNumber( m_cache.number ).lower();
|
|
break;
|
|
case STYLE_ROM_NUM_U:
|
|
text = makeRomanNumber( m_cache.number ).upper();
|
|
break;
|
|
case STYLE_CUSTOM:
|
|
////// TODO
|
|
default: // shut up compiler
|
|
text.setNum( m_cache.number );
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch ( m_style )
|
|
{
|
|
// --- these are used in export filters but are ignored by KoTextParag::drawLabel (for bulleted lists - which they are :)) ---
|
|
case KoParagCounter::STYLE_DISCBULLET:
|
|
text = '*';
|
|
break;
|
|
case KoParagCounter::STYLE_SQUAREBULLET:
|
|
text = '#';
|
|
break;
|
|
case KoParagCounter::STYLE_BOXBULLET:
|
|
text = '='; // think up a better character
|
|
break;
|
|
case KoParagCounter::STYLE_CIRCLEBULLET:
|
|
text = 'o';
|
|
break;
|
|
case KoParagCounter::STYLE_CUSTOMBULLET:
|
|
text = m_customBulletChar;
|
|
break;
|
|
default: // shut up compiler
|
|
break;
|
|
}
|
|
}
|
|
return text;
|
|
}
|
|
|
|
// Return the full text to be displayed
|
|
TQString KoParagCounter::text( const KoTextParag *paragraph )
|
|
{
|
|
// Return cached value if possible.
|
|
if ( !m_cache.text.isNull() )
|
|
return m_cache.text;
|
|
|
|
// If necessary, grab the text of the preceding levels.
|
|
if ( m_displayLevels > 1 && m_numbering != NUM_NONE )
|
|
{
|
|
KoTextParag* p = tqparent( paragraph );
|
|
int displayLevels = TQMIN( m_displayLevels, m_depth+1 ); // can't be >depth+1
|
|
for ( int level = 1 ; level < displayLevels ; ++level ) {
|
|
//kdDebug() << "additional level=" << level << "/" << displayLevels-1 << endl;
|
|
if ( p )
|
|
{
|
|
KoParagCounter* counter = p->counter();
|
|
TQString str = counter->levelText( p );
|
|
// If the preceding level is a bullet, replace it with blanks.
|
|
if ( counter->isBullet() )
|
|
for ( unsigned i = 0; i < str.length(); i++ )
|
|
str[i] = ' ';
|
|
|
|
str.append('.'); // hardcoded on purpose (like OO) until anyone complains
|
|
|
|
// Find the number of missing tqparents, and add dummy text for them.
|
|
int missingParents = m_depth - level - p->counter()->m_depth;
|
|
//kdDebug() << "levelText = " << str << " missingParents=" << missingParents << endl;
|
|
level += missingParents;
|
|
for ( ; missingParents > 0 ; --missingParents )
|
|
// Each missing level adds a "0"
|
|
str.append( "0." );
|
|
|
|
m_cache.text.prepend( str );
|
|
// Prepare next iteration
|
|
if ( level < displayLevels ) // no need to calc it if we won't use it
|
|
p = counter->tqparent( p );
|
|
}
|
|
else // toplevel tqparents are missing
|
|
{
|
|
// Special case for one-paragraph-documents like preview widgets
|
|
KoTextDocument* textdoc = paragraph->textDocument();
|
|
if ( paragraph == textdoc->firstParag() && paragraph == textdoc->lastParag() )
|
|
m_cache.text.prepend( "1." );
|
|
else
|
|
m_cache.text.prepend( "0." );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//kdDebug() << "result: " << m_cache.text << " + " << levelText( paragraph ) << endl;
|
|
// Now add text for this level.
|
|
m_cache.text.append( levelText( paragraph ) );
|
|
|
|
// Now apply prefix and suffix
|
|
// We want the '.' to be before the number in a RTL parag,
|
|
// but we can't paint the whole string using TQPainter::RTL direction, otherwise
|
|
// '10' becomes '01'.
|
|
m_cache.text.prepend( paragraph->string()->isRightToLeft() ? suffix() : prefix() );
|
|
m_cache.text.append( paragraph->string()->isRightToLeft() ? prefix() : suffix() );
|
|
return m_cache.text;
|
|
}
|
|
|
|
int KoParagCounter::width( const KoTextParag *paragraph )
|
|
{
|
|
// Return cached value if possible.
|
|
if ( m_cache.width != -1 && counterFormat( paragraph ) == m_cache.counterFormat )
|
|
return m_cache.width;
|
|
|
|
// Ensure paragraph text is valid.
|
|
if ( m_cache.text.isNull() )
|
|
text( paragraph );
|
|
|
|
// Now calculate width.
|
|
if ( m_cache.counterFormat )
|
|
m_cache.counterFormat->removeRef();
|
|
m_cache.counterFormat = counterFormat( paragraph );
|
|
m_cache.counterFormat->addRef();
|
|
m_cache.width = 0;
|
|
if ( m_style != STYLE_NONE || m_numbering == NUM_FOOTNOTE)
|
|
{
|
|
TQString text = m_cache.text;
|
|
if ( m_style == STYLE_CUSTOMBULLET && !text.isEmpty() )
|
|
{
|
|
text.append( " " ); // append two trailing spaces, see KoTextParag::drawLabel
|
|
}
|
|
else if ( !text.isEmpty() )
|
|
text.append( ' ' ); // append a trailing space, see KoTextParag::drawLabel
|
|
TQFontMetrics fm = m_cache.counterFormat->refFontMetrics();
|
|
for ( unsigned int i = 0; i < text.length(); i++ )
|
|
//m_cache.width += m_cache.counterFormat->width( text, i );
|
|
m_cache.width += fm.width( text[i] );
|
|
}
|
|
// Now go from 100%-zoom to LU
|
|
m_cache.width = KoTextZoomHandler::ptToLayoutUnitPt( m_cache.width );
|
|
|
|
//kdDebug(32500) << "KoParagCounter::width recalculated parag=" << paragraph << " text='" << text << "' width=" << m_cache.width << endl;
|
|
return m_cache.width;
|
|
}
|
|
|
|
int KoParagCounter::bulletX()
|
|
{
|
|
// width() must have been called first
|
|
Q_ASSERT( m_cache.width != -1 );
|
|
Q_ASSERT( m_cache.counterFormat );
|
|
int x = 0;
|
|
TQFontMetrics fm = m_cache.counterFormat->refFontMetrics();
|
|
TQString text = prefix();
|
|
for ( unsigned int i = 0; i < text.length(); i++ )
|
|
x += fm.width( text[i] );
|
|
// Now go from 100%-zoom to LU
|
|
return KoTextZoomHandler::ptToLayoutUnitPt( x );
|
|
}
|
|
|
|
// Only exists to centralize code. Does no caching.
|
|
KoTextFormat* KoParagCounter::counterFormat( const KoTextParag *paragraph )
|
|
{
|
|
KoTextFormat* refFormat = paragraph->at( 0 )->format();
|
|
KoTextFormat format( *refFormat );
|
|
format.setVAlign( KoTextFormat::AlignNormal );
|
|
return paragraph->textDocument()->formatCollection()->format( &format );
|
|
/*paragraph->paragFormat()*/
|
|
}
|
|
|
|
///
|
|
|
|
const TQCString RNUnits[] = {"", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"};
|
|
const TQCString RNTens[] = {"", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"};
|
|
const TQCString RNHundreds[] = {"", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"};
|
|
const TQCString RNThousands[] = {"", "m", "mm", "mmm"};
|
|
|
|
TQString KoParagCounter::makeRomanNumber( int n )
|
|
{
|
|
if ( n >= 0 )
|
|
return TQString::tqfromLatin1( RNThousands[ ( n / 1000 ) ] +
|
|
RNHundreds[ ( n / 100 ) % 10 ] +
|
|
RNTens[ ( n / 10 ) % 10 ] +
|
|
RNUnits[ ( n ) % 10 ] );
|
|
else { // should never happen, but better not crash if it does
|
|
kdWarning(32500) << "makeRomanNumber: n=" << n << endl;
|
|
return TQString::number( n );
|
|
}
|
|
}
|
|
|
|
TQString KoParagCounter::makeAlphaUpperNumber( int n )
|
|
{
|
|
TQString tmp;
|
|
char bottomDigit;
|
|
while ( n > 26 )
|
|
{
|
|
bottomDigit = (n-1) % 26;
|
|
n = (n-1) / 26;
|
|
tmp.prepend( TQChar( 'A' + bottomDigit ) );
|
|
}
|
|
tmp.prepend( TQChar( 'A' + n -1 ) );
|
|
return tmp;
|
|
}
|
|
|
|
TQString KoParagCounter::makeAlphaLowerNumber( int n )
|
|
{
|
|
TQString tmp;
|
|
char bottomDigit;
|
|
while ( n > 26 )
|
|
{
|
|
bottomDigit = (n-1) % 26;
|
|
n = (n-1) / 26;
|
|
tmp.prepend( TQChar( 'a' + bottomDigit ) );
|
|
}
|
|
tmp.prepend( TQChar( 'a' + n - 1 ) );
|
|
return tmp;
|
|
}
|
|
|
|
int KoParagCounter::fromRomanNumber( const TQString &string )
|
|
{
|
|
int ret = 0;
|
|
int stringStart = 0;
|
|
const int stringLen = string.length();
|
|
|
|
for (int base = 1000; base >= 1 && stringStart < stringLen; base /= 10)
|
|
{
|
|
const TQCString *rn;
|
|
int rnNum;
|
|
switch (base)
|
|
{
|
|
case 1000:
|
|
rn = RNThousands;
|
|
rnNum = sizeof (RNThousands) / sizeof (const TQCString);
|
|
break;
|
|
case 100:
|
|
rn = RNHundreds;
|
|
rnNum = sizeof (RNHundreds) / sizeof (const TQCString);
|
|
break;
|
|
case 10:
|
|
rn = RNTens;
|
|
rnNum = sizeof (RNTens) / sizeof (const TQCString);
|
|
break;
|
|
case 1:
|
|
default:
|
|
rn = RNUnits;
|
|
rnNum = sizeof (RNUnits) / sizeof (const TQCString);
|
|
break;
|
|
}
|
|
|
|
// I _think_ this will work :) - Clarence
|
|
for (int i = rnNum - 1; i >= 1; i--)
|
|
{
|
|
const int rnLength = rn[i].length();
|
|
if (string.mid(stringStart,rnLength) == (const char*)rn[i])
|
|
{
|
|
ret += i * base;
|
|
stringStart += rnLength;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (ret == 0 || stringStart != stringLen) ? -1 /*invalid value*/ : ret;
|
|
}
|
|
|
|
int KoParagCounter::fromAlphaUpperNumber( const TQString &string )
|
|
{
|
|
int ret = 0;
|
|
|
|
const int len = string.length();
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
const int add = char(string[i]) - 'A' + 1;
|
|
|
|
if (add >= 1 && add <= 26) // _not_ < 26
|
|
ret = ret * 26 + add;
|
|
else
|
|
{
|
|
ret = -1; // invalid character
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (ret == 0) ? -1 /*invalid value*/ : ret;
|
|
}
|
|
|
|
int KoParagCounter::fromAlphaLowerNumber( const TQString &string )
|
|
{
|
|
int ret = 0;
|
|
|
|
const int len = string.length();
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
const int add = char(string[i]) - 'a' + 1;
|
|
|
|
if (add >= 1 && add <= 26) // _not_ < 26
|
|
ret = ret * 26 + add;
|
|
else
|
|
{
|
|
ret = -1; // invalid character
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (ret == 0) ? -1 /*invalid value*/ : ret;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
void KoParagCounter::printRTDebug( KoTextParag* parag )
|
|
{
|
|
TQString additionalInfo;
|
|
if ( restartCounter() )
|
|
additionalInfo = "[restartCounter]";
|
|
if ( m_style == STYLE_CUSTOMBULLET )
|
|
additionalInfo += " [customBullet: " + TQString::number( m_customBulletChar.tqunicode() )
|
|
+ " in font '" + m_customBulletFont + "']";
|
|
static const char * const s_numbering[] = { "List", "Chapter", "None", "Footnote" };
|
|
kdDebug(32500) << " Counter style=" << style()
|
|
<< " numbering=" << s_numbering[ numbering() ]
|
|
<< " depth=" << depth()
|
|
<< " number=" << number( parag )
|
|
<< " text='" << text( parag ) << "'"
|
|
<< " width=" << width( parag )
|
|
<< additionalInfo << endl;
|
|
}
|
|
#endif
|