00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include <QList>
00027 #include <cassert>
00028 #include "fpoint.h"
00029 #include "scfonts.h"
00030 #include "scribusdoc.h"
00031 #include "sctext_shared.h"
00032 #include "storytext.h"
00033
00034 #include "scribus.h"
00035 #include "util.h"
00036 #include "resourcecollection.h"
00037 #include "desaxe/saxiohelper.h"
00038 #include "desaxe/digester.h"
00039 #include "desaxe/simple_actions.h"
00040
00041
00042 StoryText::StoryText(ScribusDoc * doc_) : doc(doc_)
00043 {
00044 if (doc_) {
00045 d = new ScText_Shared(&doc_->paragraphStyles());
00046 doc->paragraphStyles().connect(this, SLOT(invalidateAll()));
00047 doc->charStyles().connect(this, SLOT(invalidateAll()));
00048 }
00049 else {
00050 d = new ScText_Shared(NULL);
00051 }
00052 selFirst = 0;
00053 selLast = -1;
00054
00055 firstFrameItem = 0;
00056 lastFrameItem = -1;
00057 m_magicX = 0.0;
00058 m_lastMagicPos = -1;
00059
00060 d->len = 0;
00061 invalidateAll();
00062 }
00063
00064 StoryText::StoryText() : doc(NULL)
00065 {
00066 d = new ScText_Shared(NULL);
00067
00068 selFirst = 0;
00069 selLast = -1;
00070
00071 firstFrameItem = 0;
00072 lastFrameItem = -1;
00073 m_magicX = 0.0;
00074 m_lastMagicPos = -1;
00075 }
00076
00077 StoryText::StoryText(const StoryText & other) : QObject(), SaxIO(), doc(other.doc)
00078 {
00079 d = other.d;
00080 d->refs++;
00081
00082 if(doc) {
00083 doc->paragraphStyles().connect(this, SLOT(invalidateAll()));
00084 doc->charStyles().connect(this, SLOT(invalidateAll()));
00085 }
00086
00087 selFirst = 0;
00088 selLast = -1;
00089
00090 firstFrameItem = 0;
00091 lastFrameItem = -1;
00092 m_magicX = 0.0;
00093 m_lastMagicPos = -1;
00094
00095 invalidateLayout();
00096 }
00097
00098 StoryText::~StoryText()
00099 {
00100
00101
00102
00103
00104
00105
00106
00107
00108 d->refs--;
00109 if (d->refs == 0) {
00110 d->clear();
00111 d->len = 0;
00112 delete d;
00113 }
00114 }
00115
00116 StoryText StoryText::copy() const
00117 {
00118 StoryText result(doc);
00119 *(result.d) = *d;
00120 return result;
00121
00122 QListIterator<ScText*> it( *(result.d) );
00123 ScText* elem;
00124 while ( it.hasNext() ) {
00125 elem = it.next();
00126
00127 }
00128
00129 return result;
00130 }
00131
00132
00133 StoryText& StoryText::operator= (const StoryText & other)
00134 {
00135 other.d->refs++;
00136
00137 d->refs--;
00138 if (d->refs == 0) {
00139 clear();
00140 delete d;
00141 }
00142
00143 if(doc) {
00144 doc->paragraphStyles().disconnect(this, SLOT(invalidateAll()));
00145 doc->charStyles().disconnect(this, SLOT(invalidateAll()));
00146 }
00147
00148 doc = other.doc;
00149 d = other.d;
00150
00151 if (doc) {
00152 doc->paragraphStyles().connect(this, SLOT(invalidateAll()));
00153 doc->charStyles().connect(this, SLOT(invalidateAll()));
00154 }
00155
00156 selFirst = 0;
00157 selLast = -1;
00158
00159 firstFrameItem = 0;
00160 lastFrameItem = -1;
00161
00162 invalidateLayout();
00163 return *this;
00164 }
00165
00166
00167 void StoryText::clear()
00168 {
00169 selFirst = 0;
00170 selLast = -1;
00171
00172 firstFrameItem = 0;
00173 lastFrameItem = -1;
00174
00175 d->defaultStyle.erase();
00176 d->trailingStyle.erase();
00177
00178 d->clear();
00179 d->len = 0;
00180 invalidateAll();
00181 }
00182
00183
00184 void StoryText::insert(int pos, const StoryText& other, bool onlySelection)
00185 {
00186 if (pos < 0)
00187 pos += length()+1;
00188
00189 CharStyle cstyle(charStyle(pos));
00190 ParagraphStyle pstyle(paragraphStyle(pos));
00191
00192
00193 ParagraphStyle otherDefault(other.defaultStyle());
00194 otherDefault.eraseStyle(defaultStyle());
00195
00196 int otherStart = onlySelection? other.startOfSelection() : 0;
00197 int otherEnd = onlySelection? other.endOfSelection() : other.length();
00198 int cstyleStart = otherStart;
00199 for (int i=otherStart; i < otherEnd; ++i) {
00200 if (other.charStyle(i) == cstyle
00201 && other.text(i) != SpecialChars::OBJECT
00202 && other.text(i) != SpecialChars::PARSEP)
00203 continue;
00204 int len = i - cstyleStart;
00205 if (len > 0) {
00206 insertChars(pos, other.text(cstyleStart, len));
00207 applyCharStyle(pos, len, otherDefault.charStyle());
00208 applyCharStyle(pos, len, cstyle);
00209 pos += len;
00210 }
00211 if (other.text(i) == SpecialChars::PARSEP) {
00212 insertChars(pos, SpecialChars::PARSEP);
00213 applyStyle(pos, otherDefault);
00214 applyStyle(pos, other.paragraphStyle(i));
00215 cstyleStart = i+1;
00216 pos += 1;
00217 }
00218 else if (other.text(i) == SpecialChars::OBJECT) {
00219 insertChars(pos, SpecialChars::OBJECT);
00220 item(pos)->embedded = other.item(i)->embedded;
00221 cstyleStart = i+1;
00222 pos += 1;
00223 }
00224 else {
00225 cstyle = other.charStyle(i);
00226 cstyleStart = i;
00227 }
00228 }
00229 int len = otherEnd - cstyleStart;
00230 if (len > 0) {
00231 insertChars(pos, other.text(cstyleStart, len));
00232 applyCharStyle(pos, len, otherDefault.charStyle());
00233 applyCharStyle(pos, len, cstyle);
00234 pos += len;
00235 if (other.text(otherEnd-1) != SpecialChars::PARSEP) {
00236 applyStyle(pos, otherDefault);
00237 applyStyle(pos, other.paragraphStyle(otherEnd-1));
00238 }
00239 }
00240 invalidate(pos, length());
00241 }
00242
00243
00247 void StoryText::insertParSep(int pos)
00248 {
00249 ScText* it = item_p(pos);
00250 if(!it->parstyle) {
00251 it->parstyle = new ParagraphStyle(paragraphStyle(pos+1));
00252 it->parstyle->setContext( & d->pstyleContext);
00253
00254
00255
00256 }
00257 d->replaceCharStyleContextInParagraph(pos, it->parstyle->charStyleContext());
00258 }
00263 void StoryText::removeParSep(int pos)
00264 {
00265 ScText* it = item_p(pos);
00266 if (it->parstyle) {
00267
00268
00269
00270 delete it->parstyle;
00271 it->parstyle = 0;
00272 }
00273
00274
00275 it->ch = 0;
00276 d->replaceCharStyleContextInParagraph(pos, paragraphStyle(pos+1).charStyleContext());
00277 }
00278
00279 void StoryText::removeChars(int pos, uint len)
00280 {
00281 if (pos < 0)
00282 pos += length();
00283
00284 assert( len > 0 );
00285 assert( pos >= 0 );
00286 assert( pos + static_cast<int>(len) <= length() );
00287
00288 for ( int i=pos + static_cast<int>(len) - 1; i >= pos; --i )
00289 {
00290 ScText *it = d->at(i);
00291 if ((it->ch == SpecialChars::PARSEP)) {
00292 removeParSep(i);
00293 }
00294
00295 d->takeAt(i);
00296 d->len--;
00297 delete it;
00298 }
00299
00300 d->len = d->count();
00301 invalidate(pos, length());
00302 }
00303
00304 void StoryText::insertChars(int pos, QString txt, bool applyNeighbourStyle)
00305 {
00306 if (pos < 0)
00307 pos += length()+1;
00308
00309 assert(pos >= 0);
00310 assert(pos <= length());
00311
00312 if (txt.length() == 0)
00313 return;
00314
00315 const StyleContext* cStyleContext = paragraphStyle(pos).charStyleContext();
00316
00317 ScText clone;
00318 if (applyNeighbourStyle)
00319 {
00320 int referenceChar = qMax(0, qMin(pos, length()-1));
00321 clone.applyCharStyle(charStyle(referenceChar));
00322 clone.setEffects(ScStyle_Default);
00323 }
00324
00325 for (int i = 0; i < txt.length(); ++i) {
00326 ScText * item = new ScText(clone);
00327 item->ch= txt.at(i);
00328 item->setContext(cStyleContext);
00329 d->insert(pos + i, item);
00330 d->len++;
00331 if (item->ch == SpecialChars::PARSEP) {
00332
00333 insertParSep(pos + i);
00334 }
00335 }
00336
00337 d->len = d->count();
00338 invalidate(pos, pos + txt.length());
00339 }
00340
00341 void StoryText::replaceChar(int pos, QChar ch)
00342 {
00343 if (pos < 0)
00344 pos += length();
00345
00346 assert(pos >= 0);
00347 assert(pos < length());
00348
00349 ScText* item = d->at(pos);
00350 if (item->ch == ch)
00351 return;
00352
00353 if (d->at(pos)->ch == SpecialChars::PARSEP) {
00354 removeParSep(pos);
00355 }
00356 item->ch = ch;
00357 if (d->at(pos)->ch == SpecialChars::PARSEP) {
00358 insertParSep(pos);
00359 }
00360
00361 invalidate(pos, pos + 1);
00362 }
00363
00364 void StoryText::hyphenateWord(int pos, uint len, char* hyphens)
00365 {
00366 assert(pos >= 0);
00367 assert(pos + signed(len) <= length());
00368
00369
00370 for (int i=pos; i < pos+signed(len); ++i)
00371 {
00372
00373 if(hyphens && hyphens[i-pos] & 1) {
00374 d->at(i)->setEffects(d->at(i)->effects() | ScStyle_HyphenationPossible);
00375
00376 }
00377 else {
00378 d->at(i)->setEffects(d->at(i)->effects() & ~ScStyle_HyphenationPossible);
00379 }
00380 }
00381
00382 invalidate(pos, pos + len);
00383 }
00384
00385 void StoryText::insertObject(int pos, PageItem* ob)
00386 {
00387 if (pos < 0)
00388 pos += length()+1;
00389
00390 insertChars(pos, SpecialChars::OBJECT);
00391 const_cast<StoryText *>(this)->d->at(pos)->embedded = InlineFrame(ob);
00392 ob->isEmbedded = true;
00393 }
00394
00395
00396 int StoryText::length() const
00397 {
00398 return d->len;
00399 }
00400
00401 QChar StoryText::text(int pos) const
00402 {
00403 if (pos < 0)
00404 pos += length();
00405
00406 assert(pos >= 0);
00407 assert(pos < length());
00408
00409 return const_cast<StoryText *>(this)->d->at(pos)->ch;
00410 }
00411
00412 QString StoryText::text(int pos, uint len) const
00413 {
00414 if (pos < 0)
00415 pos += length();
00416
00417 assert(pos >= 0);
00418 assert(pos + signed(len) <= length());
00419
00420 QString result;
00421 StoryText* that(const_cast<StoryText*>(this));
00422 for (int i = pos; i < pos+signed(len); ++i) {
00423 result += that->d->at(i)->ch;
00424 }
00425
00426 return result;
00427 }
00428
00429 PageItem* StoryText::object(int pos) const
00430 {
00431 if (pos < 0)
00432 pos += length();
00433
00434 assert(pos >= 0);
00435 assert(pos < length());
00436
00437 StoryText* that = const_cast<StoryText *>(this);
00438 return that->d->at(pos)->embedded.getItem();
00439 }
00440
00441
00442 const CharStyle & StoryText::charStyle(int pos) const
00443 {
00444 if (pos < 0)
00445 pos += length();
00446
00447 assert(pos >= 0);
00448 assert(pos <= length());
00449
00450 if (length() == 0) {
00451
00452 return defaultStyle().charStyle();
00453 }
00454 else if (pos == length()) {
00455 qDebug("storytext::charstyle: access at end of text %i", pos);
00456 --pos;
00457 }
00458 if (text(pos) == SpecialChars::PARSEP)
00459 return paragraphStyle(pos).charStyle();
00460
00461 StoryText* that = const_cast<StoryText *>(this);
00462 return dynamic_cast<const CharStyle &> (*that->d->at(pos));
00463 }
00464
00465 const ParagraphStyle & StoryText::paragraphStyle(int pos) const
00466 {
00467 if (pos < 0)
00468 pos += length();
00469
00470 assert(pos >= 0);
00471 assert(pos <= length());
00472
00473 assert(d);
00474
00475 StoryText * that = const_cast<StoryText *> (this);
00476
00477
00478
00479
00480 while (pos < length() && that->d->at(pos)->ch != SpecialChars::PARSEP) {
00481 ++pos;
00482 }
00483 if (pos >= length()) {
00484 return that->d->trailingStyle;
00485 }
00486 else if ( !that->d->at(pos)->parstyle ) {
00487 ScText* current = that->d->at(pos);
00488 qDebug("inserting default parstyle at %i", pos);
00489 current->parstyle = new ParagraphStyle();
00490 current->parstyle->setContext( & d->pstyleContext);
00491
00492
00493
00494 }
00495 else {
00496
00497 }
00498 assert (that->d->at(pos)->parstyle);
00499 return *that->d->at(pos)->parstyle;
00500 }
00501
00502 const ParagraphStyle& StoryText::defaultStyle() const
00503 {
00504 assert(d);
00505 return d->defaultStyle;
00506 }
00507
00508
00509 void StoryText::setDefaultStyle(const ParagraphStyle& style)
00510 {
00511 const StyleContext * oldPContext = d->defaultStyle.context();
00512
00513 d->defaultStyle = style;
00514 d->defaultStyle.setContext( oldPContext );
00515
00516
00517
00518
00519
00520
00521
00522 invalidateAll();
00523 }
00524
00525
00526 void StoryText::applyCharStyle(int pos, uint len, const CharStyle& style )
00527 {
00528 if (pos < 0)
00529 pos += length();
00530
00531 assert(pos >= 0);
00532 assert(pos + signed(len) <= length());
00533
00534 if (len == 0)
00535 return;
00536
00537 ScText* itText;
00538 for (uint i=pos; i < pos+len; ++i) {
00539 itText = d->at(i);
00540 if (itText->ch == SpecialChars::PARSEP && itText->parstyle != NULL)
00541 itText->parstyle->charStyle().applyCharStyle(style);
00542 itText->applyCharStyle(style);
00543 }
00544
00545 invalidate(pos, pos + len);
00546 }
00547
00548
00549
00550 void StoryText::eraseCharStyle(int pos, uint len, const CharStyle& style )
00551 {
00552 if (pos < 0)
00553 pos += length();
00554
00555 assert(pos >= 0);
00556 assert(pos + signed(len) <= length());
00557
00558 if (len == 0)
00559 return;
00560
00561 ScText* itText;
00562 for (uint i=pos; i < pos+len; ++i) {
00563 itText = d->at(i);
00564 if (itText->ch == SpecialChars::PARSEP && itText->parstyle != NULL)
00565 itText->parstyle->charStyle().eraseCharStyle(style);
00566 itText->eraseCharStyle(style);
00567 }
00568
00569 invalidate(pos, pos + len);
00570 }
00571
00572
00573 void StoryText::applyStyle(int pos, const ParagraphStyle& style)
00574 {
00575 if (pos < 0)
00576 pos += length();
00577
00578 assert(pos >= 0);
00579 assert(pos <= length());
00580
00581 int i = pos;
00582 while (i < length() && d->at(i)->ch != SpecialChars::PARSEP) {
00583 ++i;
00584 }
00585 if (i < length()) {
00586 if (!d->at(i)->parstyle) {
00587 qDebug("PARSEP without style at pos %i", i);
00588 d->at(i)->parstyle = new ParagraphStyle();
00589 d->at(i)->parstyle->setContext( & d->pstyleContext);
00590
00591
00592
00593 }
00594
00595 d->at(i)->parstyle->applyStyle(style);
00596 }
00597 else {
00598
00599
00600 d->trailingStyle.applyStyle(style);
00601 }
00602 invalidate(pos, qMin(i, length()));
00603 }
00604
00605 void StoryText::eraseStyle(int pos, const ParagraphStyle& style)
00606 {
00607 if (pos < 0)
00608 pos += length();
00609
00610 assert(pos >= 0);
00611 assert(pos <= length());
00612
00613 int i = pos;
00614 while (i < length() && d->at(i)->ch != SpecialChars::PARSEP) {
00615 ++i;
00616 }
00617 if (i < length()) {
00618 if (!d->at(i)->parstyle) {
00619 qDebug("PARSEP without style at pos %i", i);
00620 d->at(i)->parstyle = new ParagraphStyle();
00621 d->at(i)->parstyle->setContext( & d->pstyleContext);
00622
00623
00624
00625 }
00626
00627 d->at(i)->parstyle->eraseStyle(style);
00628 }
00629 else {
00630
00631
00632 d->trailingStyle.eraseStyle(style);
00633 }
00634 invalidate(pos, qMin(i, length()));
00635 }
00636
00637
00638 void StoryText::setStyle(int pos, const ParagraphStyle& style)
00639 {
00640 eraseStyle(pos, paragraphStyle(pos));
00641 applyStyle(pos, style);
00642 }
00643
00644
00645 void StoryText::setCharStyle(int pos, uint len, const CharStyle& style)
00646 {
00647 if (pos < 0)
00648 pos += length();
00649
00650 assert(pos >= 0);
00651 assert(len <= unsigned(length()));
00652 assert(pos + signed(len) <= length());
00653
00654 if (len == 0)
00655 return;
00656
00657 ScText* itText;
00658 for (uint i=pos; i < pos+len; ++i) {
00659 itText = d->at(i);
00660 if (itText->ch == SpecialChars::PARSEP && itText->parstyle != NULL)
00661 itText->parstyle->charStyle() = style;
00662 itText->setStyle(style);
00663 }
00664
00665 invalidate(pos, pos + len);
00666 }
00667
00668
00669
00670 void StoryText::getNamedResources(ResourceCollection& lists) const
00671 {
00672 d->defaultStyle.getNamedResources(lists);
00673 d->trailingStyle.getNamedResources(lists);
00674
00675 for (int i=0; i < length(); ++i)
00676 {
00677 if (text(i) == SpecialChars::PARSEP)
00678 paragraphStyle(i).getNamedResources(lists);
00679 else if (text(i) == SpecialChars::OBJECT)
00680 object(i)->getNamedResources(lists);
00681 else
00682 charStyle(i).getNamedResources(lists);
00683 }
00684 }
00685
00686
00687 void StoryText::replaceStyles(QMap<QString,QString> newNameForOld)
00688 {
00689 ResourceCollection newnames;
00690 newnames.mapStyles(newNameForOld);
00691 replaceNamedResources(newnames);
00692 }
00693
00694 void StoryText::replaceNamedResources(ResourceCollection& newNames)
00695 {
00696 int len = length();
00697
00698 d->defaultStyle.replaceNamedResources(newNames);
00699 d->trailingStyle.replaceNamedResources(newNames);
00700
00701 if (len == 0)
00702 return;
00703
00704 ScText* itText;
00705 for (int i=0; i < len; ++i) {
00706 itText = d->at(i);
00707 if (itText->parstyle)
00708 itText->parstyle->replaceNamedResources(newNames);
00709 else
00710 itText->replaceNamedResources(newNames);
00711 }
00712
00713 invalidate(0, len);
00714 }
00715
00716
00717 void StoryText::replaceCharStyles(QMap<QString,QString> newNameForOld)
00718 {
00719 ResourceCollection newnames;
00720 newnames.mapCharStyles(newNameForOld);
00721 replaceNamedResources(newnames);
00722 }
00723
00724
00725 int StoryText::nrOfParagraph(uint index) const
00726 {
00727 int result = 0;
00728 StoryText* that = const_cast<StoryText *>(this);
00729 bool lastWasPARSEP = true;
00730 index = qMin(index, (uint) that->length());
00731 for (uint i=0; i < index; ++i) {
00732 lastWasPARSEP = that->d->at(i)->ch == SpecialChars::PARSEP;
00733 if (lastWasPARSEP)
00734 ++result;
00735 }
00736 return result;
00737 }
00738
00739 uint StoryText::nrOfParagraphs() const
00740 {
00741 uint result = 0;
00742 StoryText* that = const_cast<StoryText *>(this);
00743 bool lastWasPARSEP = true;
00744 for (int i=0; i < length(); ++i) {
00745 lastWasPARSEP = that->d->at(i)->ch == SpecialChars::PARSEP;
00746 if (lastWasPARSEP)
00747 ++result;
00748 }
00749 return lastWasPARSEP ? result : result + 1;
00750 }
00751
00752 int StoryText::startOfParagraph(uint index) const
00753 {
00754 if (index == 0)
00755 return 0;
00756
00757 StoryText* that = const_cast<StoryText *>(this);
00758 for (int i=0; i < length(); ++i) {
00759 if (that->d->at(i)->ch == SpecialChars::PARSEP && ! --index)
00760 return i + 1;
00761 }
00762 return length();
00763 }
00764
00765 int StoryText::endOfParagraph(uint index) const
00766 {
00767 ++index;
00768 StoryText* that = const_cast<StoryText *>(this);
00769 for (int i=0; i < length(); ++i) {
00770 if (that->d->at(i)->ch == SpecialChars::PARSEP && ! --index)
00771 return i;
00772 }
00773 return length();
00774 }
00775
00776 uint StoryText::nrOfRuns() const
00777 {
00778 return length();
00779 }
00780
00781 int StoryText::startOfRun(uint index) const
00782 {
00783 return index;
00784 }
00785
00786 int StoryText::endOfRun(uint index) const
00787 {
00788 return index + 1;
00789 }
00790
00791
00792
00793
00794 static QString wordBoundaries(" .,:;\"'!?\n");
00795 static QString sentenceBoundaries(".:!?\n");
00796
00797 int StoryText::nextChar(int pos)
00798 {
00799 if (pos < length())
00800 return pos+1;
00801 else
00802 return length();
00803 }
00804 int StoryText::prevChar(int pos)
00805 {
00806 if (pos > 0)
00807 return pos - 1;
00808 else
00809 return 0;
00810 }
00811 int StoryText::nextWord(int pos)
00812 {
00813 int len = length();
00814 pos = qMin(len, pos+1);
00815 while (pos < len && wordBoundaries.indexOf(text(pos)) < 0)
00816 ++pos;
00817 return pos < len ? pos + 1 : pos;
00818 }
00819 int StoryText::prevWord(int pos)
00820 {
00821 pos = qMax(0, pos-1);
00822 while (pos > 0 && wordBoundaries.indexOf(text(pos)) < 0)
00823 --pos;
00824 return wordBoundaries.indexOf(text(pos)) < 0 ? pos + 1 : pos;
00825 }
00826 int StoryText::nextSentence(int pos)
00827 {
00828 int len = length();
00829 pos = qMin(len, pos+1);
00830 while (pos < len && sentenceBoundaries.indexOf(text(pos)) < 0)
00831 ++pos;
00832 return pos < len ? pos + 1 : pos;
00833 }
00834 int StoryText::prevSentence(int pos)
00835 {
00836 pos = qMax(0, pos-1);
00837 while (pos > 0 && sentenceBoundaries.indexOf(text(pos)) < 0)
00838 --pos;
00839 return sentenceBoundaries.indexOf(text(pos)) < 0 ? pos + 1 : pos;
00840 }
00841 int StoryText::nextParagraph(int pos)
00842 {
00843 int len = length();
00844 pos = qMin(len, pos+1);
00845 while (pos < len && text(pos) != SpecialChars::PARSEP)
00846 ++pos;
00847 return pos;
00848 }
00849 int StoryText::prevParagraph(int pos)
00850 {
00851 pos = qMax(0, pos-1);
00852 while (pos > 0 && text(pos) != SpecialChars::PARSEP)
00853 --pos;
00854 return pos;
00855 }
00856
00857
00858
00859 int StoryText::startOfLine(int pos)
00860 {
00861 for (int i=0; i < m_lines.count(); ++i) {
00862 const LineSpec & ls(m_lines.at(i));
00863 if (ls.firstItem <= pos && pos <= ls.lastItem)
00864 return ls.firstItem;
00865 }
00866 return 0;
00867 }
00868 int StoryText::endOfLine(int pos)
00869 {
00870 for (int i=0; i < m_lines.count(); ++i) {
00871 const LineSpec & ls(m_lines.at(i));
00872 if (ls.firstItem <= pos && pos <= ls.lastItem)
00873 return text(ls.lastItem) == SpecialChars::PARSEP ? ls.lastItem :
00874 text(ls.lastItem) == ' ' ? ls.lastItem : ls.lastItem + 1;
00875 }
00876 return length();
00877 }
00878 int StoryText::prevLine(int pos)
00879 {
00880 for (int i=0; i < m_lines.count(); ++i)
00881 {
00882
00883 const LineSpec & ls(m_lines.at(i));
00884 if (ls.firstItem <= pos && pos <= ls.lastItem)
00885 {
00886 if (i == 0)
00887 return startOfLine(pos);
00888
00889 double xpos = 0.0;
00890 for (int j = ls.firstItem; j < pos; ++j)
00891 xpos += item(j)->glyph.wide();
00892 if (pos != m_lastMagicPos || xpos > m_magicX)
00893 m_magicX = xpos;
00894 const LineSpec & ls2(m_lines.at(i-1));
00895
00896 xpos = 0.0;
00897 for (int j = ls2.firstItem; j <= ls2.lastItem; ++j)
00898 {
00899 xpos += item(j)->glyph.wide();
00900 if (xpos > m_magicX) {
00901 m_lastMagicPos = j;
00902 return j;
00903 }
00904 }
00905 m_lastMagicPos = ls2.lastItem;
00906 return ls2.lastItem;
00907 }
00908 }
00909 return firstFrameItem;
00910 }
00911
00912 int StoryText::nextLine(int pos)
00913 {
00914 for (int i=0; i < m_lines.count(); ++i)
00915 {
00916
00917 const LineSpec & ls(m_lines.at(i));
00918 if (ls.firstItem <= pos && pos <= ls.lastItem)
00919 {
00920 if (i+1 == m_lines.count())
00921 return endOfLine(pos);
00922
00923 double xpos = 0.0;
00924 for (int j = ls.firstItem; j < pos; ++j)
00925 xpos += item(j)->glyph.wide();
00926 if (pos != m_lastMagicPos || xpos > m_magicX)
00927 m_magicX = xpos;
00928 const LineSpec & ls2(m_lines.at(i+1));
00929
00930 xpos = 0.0;
00931 for (int j = ls2.firstItem; j <= ls2.lastItem; ++j)
00932 {
00933 xpos += item(j)->glyph.wide();
00934 if (xpos > m_magicX) {
00935 m_lastMagicPos = j;
00936 return j;
00937 }
00938 }
00939 m_lastMagicPos = ls2.lastItem + 1;
00940 return ls2.lastItem + 1;
00941 }
00942 }
00943 return lastFrameItem;
00944 }
00945
00946 int StoryText::startOfFrame(int pos)
00947 {
00948 return firstFrameItem;
00949 }
00950 int StoryText::endOfFrame(int pos)
00951 {
00952 return lastFrameItem + 1;
00953 }
00954
00955
00956
00957 int StoryText::startOfSelection() const
00958 {
00959 return selFirst <= selLast? selFirst : 0;
00960 }
00961
00962 int StoryText::endOfSelection() const
00963 {
00964 return selFirst <= selLast? selLast + 1 : -1;
00965 }
00966
00967 int StoryText::lengthOfSelection() const
00968 {
00969 return selFirst <= selLast? selLast - selFirst + 1 : 0;
00970 }
00971
00972
00973 bool StoryText::selected(int pos) const
00974 {
00975 return (selFirst <= pos && pos <= selLast)
00976
00977 ;
00978 }
00979
00980 void StoryText::select(int pos, uint len, bool on)
00981 {
00982 if (pos < 0)
00983 pos += length();
00984
00985 assert( pos >= 0 );
00986 assert( pos + signed(len) <= length() );
00987
00988
00989
00990
00991