Scribus Developer Documentation - API
logo-pen

Scribus Developer Documentation - API

storytext.cpp

Go to the documentation of this file.
00001 /*
00002  For general Scribus (>=1.3.2) copyright and licensing information please refer
00003  to the COPYING file provided with the program. Following this notice may exist
00004  a copyright and/or license notice that predates the release of Scribus 1.3.2
00005  for which a new license (GPL+exception) is in place.
00006  */
00007 /***************************************************************************
00008 pageitem.cpp  -  description
00009 -------------------
00010     begin                : Sat Apr 7 2001
00011     copyright            : (C) 2001 by Franz Schmid
00012     email                : Franz.Schmid@altmuehlnet.de
00013 ***************************************************************************/
00014 
00015 /***************************************************************************
00016 *                                                                         *
00017 *   This program is free software; you can redistribute it and/or modify  *
00018 *   it under the terms of the GNU General Public License as published by  *
00019 *   the Free Software Foundation; either version 2 of the License, or     *
00020 *   (at your option) any later version.                                   *
00021 *                                                                         *
00022 ***************************************************************************/
00023 
00024 
00025 //FIXME: this include must go to sctextstruct.h !
00026 #include <QList>
00027 #include <cassert>  //added to make Fedora-5 happy
00028 #include "fpoint.h"
00029 #include "scfonts.h"
00030 #include "scribusdoc.h"
00031 #include "sctext_shared.h"
00032 #include "storytext.h"
00033 //#include "storytext.moc"
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 /* Code below is not needed imho, as all connections will be disconnected automatically
00101     by the QObject destructor. At least removing that code fixes a crash when closing
00102     documents */
00103 /*  if (doc)
00104     {
00105         doc->paragraphStyles().disconnect(this, SLOT(invalidateAll()));
00106         doc->charStyles().disconnect(this, SLOT(invalidateAll()));
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 //  qDebug(QString("StoryText::copy:"));
00122     QListIterator<ScText*> it( *(result.d) );
00123     ScText* elem;
00124     while ( it.hasNext() ) {
00125         elem = it.next();
00126 //      qDebug(QString("\tchar '%1' size %2 (orig %3)").arg(elem->ch).arg(elem->fontSize()).arg(charStyle(i++).fontSize()));
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     // this style represents all differences between this and other's defaultstyles
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 //      it->parstyle->setName("para"); // DONT TRANSLATE
00254 //      it->parstyle->charStyle().setName("cpara"); // DONT TRANSLATE
00255 //      it->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext() );
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 //      const CharStyle* oldP = & it->parstyle->charStyle();
00268 //      const CharStyle* newP = & that->paragraphStyle(pos+1).charStyle();
00269 //      d->replaceParentStyle(pos, oldP, newP);
00270         delete it->parstyle;
00271         it->parstyle = 0;
00272     }
00273     // demote this parsep so the assert code in replaceCharStyleContextInParagraph()
00274     // doesnt choke:
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 //      qDebug("remove char %d at %d", (int) it->ch.unicode(), i);
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) //, const CharStyle & charstyle)
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 //          qDebug(QString("new PARSEP %2 at %1").arg(pos).arg(paragraphStyle(pos).name()));
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 //  QString dump("");
00370     for (int i=pos; i < pos+signed(len); ++i)
00371     {
00372 //      dump += d->at(i)->ch;
00373         if(hyphens && hyphens[i-pos] & 1) {
00374             d->at(i)->setEffects(d->at(i)->effects() | ScStyle_HyphenationPossible);
00375 //          dump += "-";
00376         }
00377         else {
00378             d->at(i)->setEffects(d->at(i)->effects() & ~ScStyle_HyphenationPossible);
00379         }
00380     }
00381 //  qDebug(QString("st: %1").arg(dump));
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;   // this might not be enough...
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 //      qDebug("storytext::charstyle: default");
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 //  assert( that->at(pos)->cab >= 0 );
00477 //  assert( that->at(pos)->cab < doc->docParagraphStyles.count() );
00478 //  return doc->docParagraphStyles[that->at(pos)->cab];
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 //      current->parstyle->setName( "para(paragraphStyle)" ); // DONT TRANSLATE
00492 //      current->parstyle->charStyle().setName( "cpara(paragraphStyle)" ); // DONT TRANSLATE
00493 //      current->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext());
00494     }
00495     else {
00496 //      qDebug(QString("using parstyle at %1").arg(pos));
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 //  const StyleContext * oldCContext = d->defaultStyle.charStyle().context();
00513     d->defaultStyle = style;
00514     d->defaultStyle.setContext( oldPContext );
00515 //  d->defaultStyle.setName( "storydefault" ); // DONT TRANSLATE
00516 //  d->defaultStyle.charStyle().setName( "cstorydefault" ); // DONT TRANSLATE
00517 //  qDebug(QString("defstyle %1 context %2 defcstyle %3 ccontext %4 newcontext %5")
00518 //         .arg((uint)&d->defaultStyle,16).arg((uint)oldPContext,16)
00519 //         .arg((uint)&d->defaultStyle.charStyle(),16).arg((uint)oldCContext,16)
00520 //         .arg((uint)d->defaultStyle.charStyle().context(),16));
00521 //  d->defaultStyle.charStyle().setContext( oldCContext );
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 //          d->at(i)->parstyle->setName( "para(applyStyle)" ); // DONT TRANSLATE
00591 //          d->at(i)->parstyle->charStyle().setName( "cpara(applyStyle)" ); // DONT TRANSLATE
00592 //          d->at(i)->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext() );
00593         }
00594 //      qDebug(QString("applying parstyle %2 at %1 for %3").arg(i).arg(paragraphStyle(pos).name()).arg(pos));
00595         d->at(i)->parstyle->applyStyle(style);
00596     }
00597     else {
00598         // not happy about this but inserting a new PARSEP makes more trouble
00599 //      qDebug(QString("applying parstyle %1 as defaultstyle for %2").arg(paragraphStyle(pos).name()).arg(pos));
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 //          d->at(i)->parstyle->setName( "para(eraseStyle)" ); // DONT TRANSLATE
00623 //          d->at(i)->parstyle->charStyle().setName( "cpara(eraseStyle)" ); // DONT TRANSLATE
00624 //          d->at(i)->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext());
00625         }
00626         //      qDebug(QString("applying parstyle %2 at %1 for %3").arg(i).arg(paragraphStyle(pos).name()).arg(pos));
00627         d->at(i)->parstyle->eraseStyle(style);
00628     }
00629     else {
00630         // not happy about this but inserting a new PARSEP makes more trouble
00631         //      qDebug(QString("applying parstyle %1 as defaultstyle for %2").arg(paragraphStyle(pos).name()).arg(pos));
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 // positioning. all positioning methods return char positions
00792 // FIXME: make that methods use correct semantic boundaries
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 // these need valid layout:
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         // find line for pos
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             // find current xpos
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             // find new cpos
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         // find line for pos
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             // find current xpos
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             // find new cpos
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 // selection
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 //         || (pos >= 0 && pos < length() && const_cast<StoryText*>(this)->d->at(pos)->cselect)
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 //  qDebug("old selection: %d - %d", selFirst, selLast);
00989 
00990 //  StoryText* that = const_cast<StoryText *>(this);
00991 //  for (int i