00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "koparagcounter.h"
00021 #include "kozoomhandler.h"
00022 #include "kotextformat.h"
00023 #include "kotextdocument.h"
00024 #include <kdebug.h>
00025 #include <qdom.h>
00026
00027 static KoTextParag * const INVALID_PARAG = (KoTextParag *)-1;
00028
00029 KoParagCounter::KoParagCounter()
00030 {
00031 m_numbering = NUM_NONE;
00032 m_style = STYLE_NONE;
00033 m_depth = 0;
00034 m_startNumber = 1;
00035 m_displayLevels = 1;
00036 m_restartCounter = false;
00037 m_customBulletChar = QChar( '-' );
00038 m_customBulletFont = QString::null;
00039 m_align = Qt::AlignAuto;
00040 invalidate();
00041 }
00042
00043 bool KoParagCounter::operator==( const KoParagCounter & c2 ) const
00044 {
00045
00046 return (m_numbering==c2.m_numbering &&
00047 m_style==c2.m_style &&
00048 m_depth==c2.m_depth &&
00049 m_startNumber==c2.m_startNumber &&
00050 m_displayLevels==c2.m_displayLevels &&
00051 m_restartCounter==c2.m_restartCounter &&
00052 m_prefix==c2.m_prefix &&
00053 m_suffix==c2.m_suffix &&
00054 m_customBulletChar==c2.m_customBulletChar &&
00055 m_customBulletFont==c2.m_customBulletFont &&
00056 m_align==c2.m_align &&
00057 m_custom==c2.m_custom);
00058
00059 }
00060
00061 QString KoParagCounter::custom() const
00062 {
00063 return m_custom;
00064 }
00065
00066 QChar KoParagCounter::customBulletCharacter() const
00067 {
00068 return m_customBulletChar;
00069 }
00070
00071 QString KoParagCounter::customBulletFont() const
00072 {
00073 return m_customBulletFont;
00074 }
00075
00076 unsigned int KoParagCounter::depth() const
00077 {
00078 return m_depth;
00079 }
00080
00081 void KoParagCounter::invalidate()
00082 {
00083 m_cache.number = -1;
00084 m_cache.text = QString::null;
00085 m_cache.width = -1;
00086 m_cache.parent = INVALID_PARAG;
00087 m_cache.counterFormat = 0;
00088 }
00089
00090 bool KoParagCounter::isBullet( Style style )
00091 {
00092 switch ( style )
00093 {
00094 case STYLE_DISCBULLET:
00095 case STYLE_SQUAREBULLET:
00096 case STYLE_BOXBULLET:
00097 case STYLE_CIRCLEBULLET:
00098 case STYLE_CUSTOMBULLET:
00099 return true;
00100 default:
00101 return false;
00102 }
00103 }
00104
00105 bool KoParagCounter::isBullet() const
00106 {
00107 return isBullet( static_cast<Style>(m_style) );
00108 }
00109
00110 void KoParagCounter::load( QDomElement & element )
00111 {
00112 m_numbering = static_cast<Numbering>( element.attribute("numberingtype", "2").toInt() );
00113 m_style = static_cast<Style>( element.attribute("type").toInt() );
00114
00115 if ( (Numbering)m_numbering == NUM_LIST && (Style)m_style == STYLE_NONE )
00116 m_numbering = NUM_NONE;
00117 m_depth = element.attribute("depth").toInt();
00118 m_customBulletChar = QChar( element.attribute("bullet").toInt() );
00119 m_prefix = element.attribute("lefttext");
00120 if ( m_prefix.lower() == "(null)" )
00121 m_prefix = QString::null;
00122 m_suffix = element.attribute("righttext");
00123 if ( m_suffix.lower() == "(null)" )
00124 m_suffix = QString::null;
00125 QString s = element.attribute("start");
00126 if ( s.isEmpty() )
00127 m_startNumber = 1;
00128 else if ( s[0].isDigit() )
00129 m_startNumber = s.toInt();
00130 else
00131 m_startNumber = s.lower()[0].latin1() - 'a' + 1;
00132 s = element.attribute("display-levels");
00133 if ( !s.isEmpty() )
00134 m_displayLevels = QMIN( s.toInt(), m_depth+1 );
00135 else
00136 m_displayLevels = m_depth+1;
00137 m_customBulletFont = element.attribute("bulletfont");
00138 m_custom = element.attribute("customdef");
00139 m_align = element.attribute("align", "0").toInt();
00140 QString restart = element.attribute("restart");
00141 m_restartCounter = (restart == "true") || (restart == "1");
00142 invalidate();
00143 }
00144
00145 int KoParagCounter::number( const KoTextParag *paragraph )
00146 {
00147
00148 if ( m_cache.number != -1 )
00149 return m_cache.number;
00150
00151
00152 if ( m_restartCounter ) {
00153 m_cache.number = m_startNumber;
00154 return m_startNumber;
00155 }
00156
00157
00158
00159 KoTextParag *otherParagraph = paragraph->prev();
00160 KoParagCounter *otherCounter;
00161
00162 switch ( m_numbering )
00163 {
00164 case NUM_NONE:
00165
00166 case NUM_FOOTNOTE:
00167 m_cache.number = 0;
00168 break;
00169 case NUM_CHAPTER:
00170 m_cache.number = m_startNumber;
00171
00172 while ( otherParagraph )
00173 {
00174 otherCounter = otherParagraph->counter();
00175 if ( otherCounter &&
00176 ( (Numbering)otherCounter->m_numbering == NUM_CHAPTER ) &&
00177 ( otherCounter->m_depth <= m_depth ) )
00178 {
00179 if ( ( otherCounter->m_depth == m_depth ) &&
00180 ( otherCounter->m_style == m_style ) )
00181 {
00182
00183 m_cache.number = otherCounter->number( otherParagraph ) + 1;
00184 }
00185 else
00186 {
00187
00188 m_cache.number = m_startNumber;
00189 }
00190 break;
00191 }
00192 otherParagraph = otherParagraph->prev();
00193 }
00194 break;
00195 case NUM_LIST:
00196 m_cache.number = m_startNumber;
00197
00198 while ( otherParagraph )
00199 {
00200 otherCounter = otherParagraph->counter();
00201 if ( otherCounter )
00202 {
00203 if ( ( (Numbering)otherCounter->m_numbering == NUM_LIST ) &&
00204 !isBullet( static_cast<Style>(otherCounter->m_style) ) &&
00205 ( otherCounter->m_depth <= m_depth ) )
00206 {
00207 if ( ( otherCounter->m_depth == m_depth ) &&
00208 ( otherCounter->m_style == m_style ) )
00209 {
00210
00211 m_cache.number = otherCounter->number( otherParagraph ) + 1;
00212 }
00213 else
00214 {
00215
00216 m_cache.number = m_startNumber;
00217 }
00218 break;
00219 }
00220 else
00221 if ( (Numbering)otherCounter->m_numbering == NUM_CHAPTER )
00222 {
00223 m_cache.number = m_startNumber;
00224 break;
00225 }
00226 }
00227
00228
00229
00230
00231
00232
00233 otherParagraph = otherParagraph->prev();
00234 }
00235 break;
00236 }
00237 return m_cache.number;
00238 }
00239
00240 KoParagCounter::Numbering KoParagCounter::numbering() const
00241 {
00242 return static_cast<Numbering>(m_numbering);
00243 }
00244
00245
00246 KoTextParag *KoParagCounter::parent( const KoTextParag *paragraph )
00247 {
00248
00249 if ( m_cache.parent != INVALID_PARAG )
00250 return m_cache.parent;
00251
00252 KoTextParag *otherParagraph = paragraph->prev();
00253 KoParagCounter *otherCounter;
00254
00255
00256 switch ( (Numbering)m_numbering )
00257 {
00258 case NUM_NONE:
00259
00260 case NUM_FOOTNOTE:
00261 otherParagraph = 0L;
00262 break;
00263 case NUM_CHAPTER:
00264
00265 while ( otherParagraph )
00266 {
00267 otherCounter = otherParagraph->counter();
00268 if ( otherCounter &&
00269 ( (Numbering)otherCounter->m_numbering == NUM_CHAPTER ) &&
00270 ( otherCounter->m_depth < m_depth ) )
00271 {
00272 break;
00273 }
00274 otherParagraph = otherParagraph->prev();
00275 }
00276 break;
00277 case NUM_LIST:
00278
00279 while ( otherParagraph )
00280 {
00281 otherCounter = otherParagraph->counter();
00282 if ( otherCounter )
00283 {
00284 if ( ( (Numbering)otherCounter->m_numbering == NUM_LIST ) &&
00285 !isBullet( static_cast<Style>(otherCounter->m_style) ) &&
00286 ( otherCounter->m_depth < m_depth ) )
00287 {
00288 break;
00289 }
00290 else
00291 if ( (Numbering)otherCounter->m_numbering == NUM_CHAPTER )
00292 {
00293 otherParagraph = 0L;
00294 break;
00295 }
00296 }
00297 otherParagraph = otherParagraph->prev();
00298 }
00299 break;
00300 }
00301 m_cache.parent = otherParagraph;
00302 return m_cache.parent;
00303 }
00304
00305 QString KoParagCounter::prefix() const
00306 {
00307 return m_prefix;
00308 }
00309
00310 void KoParagCounter::save( QDomElement & element )
00311 {
00312 element.setAttribute( "type", static_cast<int>( m_style ) );
00313 element.setAttribute( "depth", m_depth );
00314 if ( (Style)m_style == STYLE_CUSTOMBULLET )
00315 {
00316 element.setAttribute( "bullet", m_customBulletChar.unicode() );
00317 if ( !m_customBulletFont.isEmpty() )
00318 element.setAttribute( "bulletfont", m_customBulletFont );
00319 }
00320 if ( !m_prefix.isEmpty() )
00321 element.setAttribute( "lefttext", m_prefix );
00322 if ( !m_suffix.isEmpty() )
00323 element.setAttribute( "righttext", m_suffix );
00324 if ( m_startNumber != 1 )
00325 element.setAttribute( "start", m_startNumber );
00326
00327 element.setAttribute( "display-levels", m_displayLevels );
00328
00329 if ( (Numbering)m_numbering != NUM_NONE && (Numbering)m_numbering != NUM_FOOTNOTE )
00330 element.setAttribute( "numberingtype", static_cast<int>( m_numbering ) );
00331 if ( !m_custom.isEmpty() )
00332 element.setAttribute( "customdef", m_custom );
00333 if ( m_restartCounter )
00334 element.setAttribute( "restart", "true" );
00335 if ( !m_cache.text.isEmpty() )
00336 element.setAttribute( "text", m_cache.text );
00337 element.setAttribute( "align", m_align );
00338 }
00339
00340 void KoParagCounter::setCustom( QString c )
00341 {
00342 m_custom = c;
00343 invalidate();
00344 }
00345
00346 void KoParagCounter::setCustomBulletCharacter( QChar c )
00347 {
00348 m_customBulletChar = c;
00349 invalidate();
00350 }
00351
00352 void KoParagCounter::setCustomBulletFont( QString f )
00353 {
00354 m_customBulletFont = f;
00355 invalidate();
00356 }
00357
00358 void KoParagCounter::setDepth( unsigned int d )
00359 {
00360 m_depth = d;
00361 invalidate();
00362 }
00363
00364 void KoParagCounter::setNumbering( Numbering n )
00365 {
00366 m_numbering = n;
00367 invalidate();
00368 }
00369
00370 void KoParagCounter::setPrefix( QString p )
00371 {
00372 m_prefix = p;
00373 invalidate();
00374 }
00375 void KoParagCounter::setStartNumber( int s )
00376 {
00377 m_startNumber = s;
00378 invalidate();
00379 }
00380
00381 void KoParagCounter::setDisplayLevels( int l )
00382 {
00383 m_displayLevels = l;
00384 invalidate();
00385 }
00386
00387 void KoParagCounter::setAlignment( int a )
00388 {
00389 m_align = a;
00390 invalidate();
00391 }
00392
00393 void KoParagCounter::setStyle( Style s )
00394 {
00395 m_style = s;
00396 invalidate();
00397 }
00398
00399 void KoParagCounter::setSuffix( QString s )
00400 {
00401 m_suffix = s;
00402 invalidate();
00403 }
00404
00405 int KoParagCounter::startNumber() const
00406 {
00407 return m_startNumber;
00408 }
00409
00410 int KoParagCounter::displayLevels() const
00411 {
00412 return m_displayLevels;
00413 }
00414
00415 int KoParagCounter::alignment() const
00416 {
00417 return m_align;
00418 }
00419
00420 KoParagCounter::Style KoParagCounter::style() const
00421 {
00422 return static_cast<Style>(m_style);
00423 }
00424
00425 QString KoParagCounter::suffix() const
00426 {
00427 return m_suffix;
00428 }
00429
00430 bool KoParagCounter::restartCounter() const
00431 {
00432 return m_restartCounter;
00433 }
00434
00435 void KoParagCounter::setRestartCounter( bool restart )
00436 {
00437 m_restartCounter = restart;
00438 invalidate();
00439 }
00440
00441
00442 QString KoParagCounter::levelText( const KoTextParag *paragraph )
00443 {
00444 bool bullet = isBullet( static_cast<Style>(m_style) );
00445
00446 if ( bullet && (Numbering)m_numbering == NUM_CHAPTER ) {
00447
00448 m_style = STYLE_NUM;
00449 bullet = false;
00450 }
00451
00452 QString text;
00453 if ( !bullet )
00454 {
00455
00456 number( paragraph );
00457
00458 switch ( m_style )
00459 {
00460 case STYLE_NONE:
00461 if ( (Numbering)m_numbering == NUM_LIST )
00462 text = ' ';
00463 break;
00464 case STYLE_NUM:
00465 text.setNum( m_cache.number );
00466 break;
00467 case STYLE_ALPHAB_L:
00468 text = makeAlphaLowerNumber( m_cache.number );
00469 break;
00470 case STYLE_ALPHAB_U:
00471 text = makeAlphaUpperNumber( m_cache.number );
00472 break;
00473 case STYLE_ROM_NUM_L:
00474 text = makeRomanNumber( m_cache.number ).lower();
00475 break;
00476 case STYLE_ROM_NUM_U:
00477 text = makeRomanNumber( m_cache.number ).upper();
00478 break;
00479 case STYLE_CUSTOM:
00480 default:
00482 text.setNum( m_cache.number );
00483 break;
00484 }
00485 }
00486 else
00487 {
00488 switch ( m_style )
00489 {
00490
00491 case KoParagCounter::STYLE_DISCBULLET:
00492 text = '*';
00493 break;
00494 case KoParagCounter::STYLE_SQUAREBULLET:
00495 text = '#';
00496 break;
00497 case KoParagCounter::STYLE_BOXBULLET:
00498 text = '=';
00499 break;
00500 case KoParagCounter::STYLE_CIRCLEBULLET:
00501 text = 'o';
00502 break;
00503 case KoParagCounter::STYLE_CUSTOMBULLET:
00504 text = m_customBulletChar;
00505 break;
00506 default:
00507 break;
00508 }
00509 }
00510 return text;
00511 }
00512
00513
00514 QString KoParagCounter::text( const KoTextParag *paragraph )
00515 {
00516
00517 if ( !m_cache.text.isNull() )
00518 return m_cache.text;
00519
00520
00521 if ( m_displayLevels > 1 )
00522 {
00523 KoTextParag* p = parent( paragraph );
00524 int displayLevels = QMIN( m_displayLevels, m_depth+1 );
00525 for ( int level = 1 ; level < displayLevels ; ++level ) {
00526
00527 if ( p )
00528 {
00529 KoParagCounter* counter = p->counter();
00530 QString str = counter->levelText( p );
00531
00532 if ( counter->isBullet() )
00533 for ( unsigned i = 0; i < str.length(); i++ )
00534 str[i] = ' ';
00535
00536 str.append('.');
00537
00538
00539 int missingParents = m_depth - level - p->counter()->m_depth;
00540
00541 level += missingParents;
00542 for ( ; missingParents > 0 ; --missingParents )
00543
00544 str.append( "0." );
00545
00546 m_cache.text.prepend( str );
00547
00548 if ( level < displayLevels )
00549 p = counter->parent( p );
00550 }
00551 else
00552 {
00553
00554 KoTextDocument* textdoc = paragraph->textDocument();
00555 if ( paragraph == textdoc->firstParag() && paragraph == textdoc->lastParag() )
00556 m_cache.text.prepend( "1." );
00557 else
00558 m_cache.text.prepend( "0." );
00559 }
00560 }
00561
00562 }
00563
00564
00565
00566 m_cache.text.append( levelText( paragraph ) );
00567
00568
00569
00570
00571
00572 m_cache.text.prepend( paragraph->string()->isRightToLeft() ? suffix() : prefix() );
00573 m_cache.text.append( paragraph->string()->isRightToLeft() ? prefix() : suffix() );
00574 return m_cache.text;
00575 }
00576
00577 int KoParagCounter::width( const KoTextParag *paragraph )
00578 {
00579
00580 if ( m_cache.width != -1 && counterFormat( paragraph ) == m_cache.counterFormat )
00581 return m_cache.width;
00582
00583
00584 if ( m_cache.text.isNull() )
00585 text( paragraph );
00586
00587
00588 if ( m_cache.counterFormat )
00589 m_cache.counterFormat->removeRef();
00590 m_cache.counterFormat = counterFormat( paragraph );
00591 m_cache.counterFormat->addRef();
00592 m_cache.width = 0;
00593 QString text = m_cache.text;
00594 if ( style() ==KoParagCounter::STYLE_CUSTOMBULLET && !text.isEmpty())
00595 {
00596 text.append( " " );
00597 }
00598 else if ( !text.isEmpty() )
00599 text.append( ' ' );
00600 QFontMetrics fm = m_cache.counterFormat->refFontMetrics();
00601 for ( unsigned int i = 0; i < text.length(); i++ )
00602
00603 m_cache.width += fm.width( text[i] );
00604
00605 m_cache.width = KoTextZoomHandler::ptToLayoutUnitPt( m_cache.width );
00606
00607
00608 return m_cache.width;
00609 }
00610
00611 int KoParagCounter::bulletX()
00612 {
00613
00614 Q_ASSERT( m_cache.width != -1 );
00615 Q_ASSERT( m_cache.counterFormat );
00616 int x = 0;
00617 QFontMetrics fm = m_cache.counterFormat->refFontMetrics();
00618 QString text = prefix();
00619 for ( unsigned int i = 0; i < text.length(); i++ )
00620 x += fm.width( text[i] );
00621
00622 return KoTextZoomHandler::ptToLayoutUnitPt( x );
00623 }
00624
00625
00626 KoTextFormat* KoParagCounter::counterFormat( const KoTextParag *paragraph )
00627 {
00628 KoTextFormat* refFormat = paragraph->at( 0 )->format();
00629 KoTextFormat format( *refFormat );
00630 format.setVAlign( KoTextFormat::AlignNormal );
00631 return paragraph->textDocument()->formatCollection()->format( &format );
00632
00633 }
00634
00636
00637 const QCString RNUnits[] = {"", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"};
00638 const QCString RNTens[] = {"", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"};
00639 const QCString RNHundreds[] = {"", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"};
00640 const QCString RNThousands[] = {"", "m", "mm", "mmm"};
00641
00642 QString KoParagCounter::makeRomanNumber( int n )
00643 {
00644 return QString::fromLatin1( RNThousands[ ( n / 1000 ) ] +
00645 RNHundreds[ ( n / 100 ) % 10 ] +
00646 RNTens[ ( n / 10 ) % 10 ] +
00647 RNUnits[ ( n ) % 10 ] );
00648 }
00649
00650 QString KoParagCounter::makeAlphaUpperNumber( int n )
00651 {
00652 QString tmp;
00653 char bottomDigit;
00654 while ( n > 26 )
00655 {
00656 bottomDigit = (n-1) % 26;
00657 n = (n-1) / 26;
00658 tmp.prepend( QChar( 'A' + bottomDigit ) );
00659 }
00660 tmp.prepend( QChar( 'A' + n -1 ) );
00661 return tmp;
00662 }
00663
00664 QString KoParagCounter::makeAlphaLowerNumber( int n )
00665 {
00666 QString tmp;
00667 char bottomDigit;
00668 while ( n > 26 )
00669 {
00670 bottomDigit = (n-1) % 26;
00671 n = (n-1) / 26;
00672 tmp.prepend( QChar( 'a' + bottomDigit ) );
00673 }
00674 tmp.prepend( QChar( 'a' + n - 1 ) );
00675 return tmp;
00676 }
00677
00678 int KoParagCounter::fromRomanNumber( const QString &string )
00679 {
00680 int ret = 0;
00681 int stringStart = 0;
00682 const int stringLen = string.length();
00683
00684 for (int base = 1000; base >= 1 && stringStart < stringLen; base /= 10)
00685 {
00686 const QCString *rn;
00687 int rnNum;
00688 switch (base)
00689 {
00690 case 1000:
00691 rn = RNThousands;
00692 rnNum = sizeof (RNThousands) / sizeof (const QCString);
00693 break;
00694 case 100:
00695 rn = RNHundreds;
00696 rnNum = sizeof (RNHundreds) / sizeof (const QCString);
00697 break;
00698 case 10:
00699 rn = RNTens;
00700 rnNum = sizeof (RNTens) / sizeof (const QCString);
00701 break;
00702 case 1:
00703 default:
00704 rn = RNUnits;
00705 rnNum = sizeof (RNUnits) / sizeof (const QCString);
00706 break;
00707 }
00708
00709
00710 for (int i = rnNum - 1; i >= 1; i--)
00711 {
00712 const int rnLength = rn[i].length();
00713 if (string.mid(stringStart,rnLength) == (const char*)rn[i])
00714 {
00715 ret += i * base;
00716 stringStart += rnLength;
00717 break;
00718 }
00719 }
00720 }
00721
00722 return (ret == 0 || stringStart != stringLen) ? -1 : ret;
00723 }
00724
00725 int KoParagCounter::fromAlphaUpperNumber( const QString &string )
00726 {
00727 int ret = 0;
00728
00729 const int len = string.length();
00730 for (int i = 0; i < len; i++)
00731 {
00732 const int add = char(string[i]) - 'A' + 1;
00733
00734 if (add >= 1 && add <= 26)
00735 ret = ret * 26 + add;
00736 else
00737 {
00738 ret = -1;
00739 break;
00740 }
00741 }
00742
00743 return (ret == 0) ? -1 : ret;
00744 }
00745
00746 int KoParagCounter::fromAlphaLowerNumber( const QString &string )
00747 {
00748 int ret = 0;
00749
00750 const int len = string.length();
00751 for (int i = 0; i < len; i++)
00752 {
00753 const int add = char(string[i]) - 'a' + 1;
00754
00755 if (add >= 1 && add <= 26)
00756 ret = ret * 26 + add;
00757 else
00758 {
00759 ret = -1;
00760 break;
00761 }
00762 }
00763
00764 return (ret == 0) ? -1 : ret;
00765 }