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 }