Back to index

scribus-ng  1.3.4.dfsg+svn20071115
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 <qvaluelist.h>
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        if (doc)
00101        {
00102               doc->paragraphStyles().disconnect(this, SLOT(invalidateAll()));
00103               doc->charStyles().disconnect(this, SLOT(invalidateAll()));
00104        }
00105        d->refs--;
00106        if (d->refs == 0) {
00107               d->clear();
00108               d->len = 0;
00109               delete d;
00110        }      
00111 }
00112 
00113 StoryText StoryText::copy() const
00114 {
00115        StoryText result(doc);
00116        *(result.d) = *d;
00117        return result;
00118 //     qDebug(QString("StoryText::copy:"));
00119        QPtrListIterator<ScText> it( *(result.d) );
00120        ScText* elem;
00121        while ( (elem = it.current()) != NULL ) {
00122               ++it;
00123 //            qDebug(QString("\tchar '%1' size %2 (orig %3)").arg(elem->ch).arg(elem->fontSize()).arg(charStyle(i++).fontSize()));
00124        }
00125        
00126        return result;
00127 }
00128 
00129 
00130 StoryText& StoryText::operator= (const StoryText & other)
00131 {
00132        other.d->refs++;
00133        
00134        d->refs--;
00135        if (d->refs == 0) {
00136               clear();
00137               delete d;
00138        }
00139        
00140        if(doc) {
00141               doc->paragraphStyles().disconnect(this, SLOT(invalidateAll()));
00142               doc->charStyles().disconnect(this, SLOT(invalidateAll()));
00143        }
00144        
00145        doc = other.doc; 
00146        d = other.d;
00147        
00148        if (doc) {
00149               doc->paragraphStyles().connect(this, SLOT(invalidateAll()));
00150               doc->charStyles().connect(this, SLOT(invalidateAll()));
00151        }
00152        
00153        selFirst = 0;
00154        selLast = -1;
00155        
00156        firstFrameItem = 0;
00157        lastFrameItem = -1;
00158 
00159        invalidateLayout();
00160        return *this;
00161 }
00162 
00163 
00164 void StoryText::clear()
00165 {
00166        selFirst = 0;
00167        selLast = -1;
00168 
00169        firstFrameItem = 0;
00170        lastFrameItem = -1;
00171        
00172        d->defaultStyle.erase();
00173        d->trailingStyle.erase();
00174        
00175        d->clear();
00176        d->len = 0;
00177        invalidateAll();
00178 }
00179 
00180 
00181 void StoryText::insert(int pos, const StoryText& other, bool onlySelection)
00182 {
00183        if (pos < 0)
00184               pos += length()+1;
00185        
00186        CharStyle cstyle(charStyle(pos));
00187        ParagraphStyle pstyle(paragraphStyle(pos));
00188        
00189        // this style represents all differences between this and other's defaultstyles
00190        ParagraphStyle otherDefault(other.defaultStyle());
00191        otherDefault.eraseStyle(defaultStyle());
00192        
00193        int otherStart  = onlySelection? other.startOfSelection() : 0;
00194        int otherEnd    = onlySelection? other.endOfSelection() : other.length();
00195        int cstyleStart = otherStart;
00196        for (int i=otherStart; i < otherEnd; ++i) {
00197               if (other.charStyle(i) == cstyle 
00198                      && other.text(i) != SpecialChars::OBJECT
00199                      && other.text(i) != SpecialChars::PARSEP)
00200                      continue;
00201               int len = i - cstyleStart;
00202               if (len > 0) {
00203                      insertChars(pos, other.text(cstyleStart, len));
00204                      applyCharStyle(pos, len, otherDefault.charStyle());
00205                      applyCharStyle(pos, len, cstyle);
00206                      pos += len;
00207               }
00208               if (other.text(i) == SpecialChars::PARSEP) {
00209                      insertChars(pos, SpecialChars::PARSEP);
00210                      applyStyle(pos, otherDefault);
00211                      applyStyle(pos, other.paragraphStyle(i));
00212                      cstyleStart = i+1;
00213                      pos += 1;
00214               }
00215               else if (other.text(i) == SpecialChars::OBJECT) {
00216                      insertChars(pos, SpecialChars::OBJECT);
00217                      item(pos)->embedded = other.item(i)->embedded;
00218                      cstyleStart = i+1;
00219                      pos += 1;
00220               }
00221               else {
00222                      cstyle = other.charStyle(i);
00223                      cstyleStart = i;
00224               }
00225        }
00226        int len = otherEnd - cstyleStart;
00227        if (len > 0) {
00228               insertChars(pos, other.text(cstyleStart, len));
00229               applyCharStyle(pos, len, otherDefault.charStyle());
00230               applyCharStyle(pos, len, cstyle);
00231               pos += len;
00232               if (other.text(otherEnd-1) != SpecialChars::PARSEP) {
00233                      applyStyle(pos, otherDefault);
00234                      applyStyle(pos, other.paragraphStyle(otherEnd-1));
00235               }
00236        }
00237        invalidate(pos, length());
00238 }
00239 
00240 
00244 void StoryText::insertParSep(int pos)
00245 {
00246        ScText* it = item_p(pos);
00247        if(!it->parstyle) {
00248               it->parstyle = new ParagraphStyle(paragraphStyle(pos+1));
00249               it->parstyle->setContext( & d->pstyleContext);
00250 //            it->parstyle->setName("para"); // DONT TRANSLATE
00251 //            it->parstyle->charStyle().setName("cpara"); // DONT TRANSLATE
00252 //            it->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext() );
00253        }
00254        d->replaceCharStyleContextInParagraph(pos, it->parstyle->charStyleContext());
00255 }
00260 void StoryText::removeParSep(int pos)
00261 {
00262        ScText* it = item_p(pos);
00263        if (it->parstyle) {
00264 //            const CharStyle* oldP = & it->parstyle->charStyle();
00265 //            const CharStyle* newP = & that->paragraphStyle(pos+1).charStyle();
00266 //            d->replaceParentStyle(pos, oldP, newP);
00267               delete it->parstyle;
00268               it->parstyle = 0;
00269        }
00270        // demote this parsep so the assert code in replaceCharStyleContextInParagraph()
00271        // doesnt choke:
00272        it->ch = "";
00273        d->replaceCharStyleContextInParagraph(pos, paragraphStyle(pos+1).charStyleContext());      
00274 }
00275 
00276 void StoryText::removeChars(int pos, uint len)
00277 {
00278        if (pos < 0)
00279               pos += length();
00280        
00281        assert( len > 0 );
00282        assert( pos >= 0 );
00283        assert( pos + static_cast<int>(len) <= length() );
00284 
00285        for ( int i=pos + static_cast<int>(len) - 1; i >= pos; --i )
00286        {
00287               ScText *it = d->at(i);
00288               if ((it->ch[0] == SpecialChars::PARSEP)) {
00289                      removeParSep(i);
00290               }
00291 //            qDebug("remove char %d at %d", it->ch[0].unicode(), i);
00292               d->take(i);
00293               d->len--;
00294               delete it;
00295        }
00296 
00297        d->len = d->count();
00298        invalidate(pos, length());
00299 }
00300 
00301 void StoryText::insertChars(int pos, QString txt, bool applyNeighbourStyle) //, const CharStyle & charstyle)
00302 {
00303        if (pos < 0)
00304               pos += length()+1;
00305 
00306        assert(pos >= 0);
00307        assert(pos <= length());
00308        
00309        if (txt.length() == 0)
00310               return;
00311        
00312        const StyleContext* cStyleContext = paragraphStyle(pos).charStyleContext();
00313        //     assert( !style.font().isNone() );
00314        
00315        
00316        ScText clone;
00317        if (applyNeighbourStyle)
00318        {
00319               int referenceChar = QMAX(0, QMIN(pos, length()-1));
00320               clone.applyCharStyle(charStyle(referenceChar));
00321               clone.setEffects(ScStyle_Default);
00322        }
00323        
00324        for (uint i = 0; i < txt.length(); ++i) {
00325               ScText * item = new ScText(clone);
00326               item->ch= txt.mid(i, 1);
00327               item->setContext(cStyleContext);
00328               d->insert(pos + i, item);
00329               d->len++;
00330               if (item->ch[0] == SpecialChars::PARSEP) {
00331 //                   qDebug(QString("new PARSEP %2 at %1").arg(pos).arg(paragraphStyle(pos).name()));
00332                      insertParSep(pos + i);
00333               }
00334        }
00335 
00336        d->len = d->count();
00337        invalidate(pos, pos + txt.length());
00338 }
00339 
00340 void StoryText::replaceChar(int pos, QChar ch)
00341 {
00342        if (pos < 0)
00343               pos += length();
00344 
00345        assert(pos >= 0);
00346        assert(pos < length());
00347 
00348        ScText* item = d->at(pos);
00349        if (item->ch[0] == ch)
00350               return;
00351        
00352        if (d->at(pos)->ch[0] == SpecialChars::PARSEP) {
00353               removeParSep(pos);
00354        }
00355        item->ch = ch;
00356        if (d->at(pos)->ch[0] == SpecialChars::PARSEP) {
00357               insertParSep(pos);
00358        }
00359        
00360        invalidate(pos, pos + 1);
00361 }
00362 
00363 void StoryText::hyphenateWord(int pos, uint len, char* hyphens)
00364 {
00365        assert(pos >= 0);
00366        assert(pos + signed(len) <= length());
00367        
00368 //     QString dump("");
00369        for (int i=pos; i < pos+signed(len); ++i)
00370        {
00371 //            dump += d->at(i)->ch;
00372               if(hyphens && hyphens[i-pos] & 1) {
00373                      d->at(i)->setEffects(d->at(i)->effects() | ScStyle_HyphenationPossible);
00374 //                   dump += "-";
00375               }
00376               else {
00377                      d->at(i)->setEffects(d->at(i)->effects() & ~ScStyle_HyphenationPossible);
00378               }
00379        }
00380 //     qDebug(QString("st: %1").arg(dump));
00381        invalidate(pos, pos + len);
00382 }
00383 
00384 void StoryText::insertObject(int pos, PageItem* ob)
00385 {
00386        if (pos < 0)
00387               pos += length()+1;
00388 
00389        insertChars(pos, SpecialChars::OBJECT);
00390        const_cast<StoryText *>(this)->d->at(pos)->embedded = InlineFrame(ob);
00391        ob->isEmbedded = true;   // this might not be enough...
00392 }
00393 
00394 
00395 int StoryText::length() const
00396 {
00397        return d->len;
00398 }
00399 
00400 QChar StoryText::text(int pos) const
00401 {
00402        if (pos < 0)
00403               pos += length();
00404 
00405        assert(pos >= 0);
00406        assert(pos < length());
00407 
00408        return const_cast<StoryText *>(this)->d->at(pos)->ch[0];
00409 }
00410 
00411 QString StoryText::text(int pos, uint len) const
00412 {
00413        if (pos < 0)
00414               pos += length();
00415 
00416        assert(pos >= 0);
00417        assert(pos + signed(len) <= length());
00418 
00419        QString result;
00420        StoryText* that(const_cast<StoryText*>(this));
00421        that->d->at(pos);
00422        for (int i = pos; i < pos+signed(len); ++i) {
00423               result += that->d->current()->ch;
00424               that->d->next();
00425        }
00426 
00427        return result;
00428 }
00429 
00430 PageItem* StoryText::object(int pos) const
00431 {
00432        if (pos < 0)
00433               pos += length();
00434 
00435        assert(pos >= 0);
00436        assert(pos < length());
00437 
00438        StoryText* that = const_cast<StoryText *>(this);
00439        return that->d->at(pos)->embedded.getItem();
00440 }
00441 
00442 
00443 const CharStyle & StoryText::charStyle(int pos) const
00444 {
00445        if (pos < 0)
00446               pos += length();
00447 
00448        assert(pos >= 0);
00449        assert(pos <= length());
00450 
00451        if (length() == 0) {
00452 //            qDebug("storytext::charstyle: default");
00453               return defaultStyle().charStyle();
00454        }
00455        else if (pos == length()) {
00456 //            qDebug(QString("storytext::charstyle: access at end of text %1").arg(pos));
00457               --pos;
00458        }
00459        if (text(pos) == SpecialChars::PARSEP)
00460               return paragraphStyle(pos).charStyle();
00461        
00462        StoryText* that = const_cast<StoryText *>(this);
00463        return dynamic_cast<const CharStyle &> (*that->d->at(pos));
00464 }
00465 
00466 const ParagraphStyle & StoryText::paragraphStyle(int pos) const
00467 {
00468        if (pos < 0)
00469               pos += length();
00470 
00471        assert(pos >= 0);
00472        assert(pos <= length());
00473 
00474        assert(d);
00475        
00476        StoryText * that = const_cast<StoryText *> (this);
00477 //     assert( that->at(pos)->cab >= 0 );
00478 //     assert( that->at(pos)->cab < doc->docParagraphStyles.count() );
00479 //     return doc->docParagraphStyles[that->at(pos)->cab];
00480        
00481        that->d->at(pos);
00482        while (pos < length() && that->d->current()->ch[0] != SpecialChars::PARSEP) {
00483               ++pos;
00484               that->d->next();
00485        }
00486        if (pos >= length()) {
00487               return that->d->trailingStyle;
00488        }
00489        else if ( !that->d->current()->parstyle ) {
00490               qDebug(QString("inserting default parstyle at %1").arg(pos));
00491               that->d->current()->parstyle = new ParagraphStyle();
00492               that->d->current()->parstyle->setContext( & d->pstyleContext);
00493 //            that->d->current()->parstyle->setName( "para(paragraphStyle)" ); // DONT TRANSLATE
00494 //            that->d->current()->parstyle->charStyle().setName( "cpara(paragraphStyle)" ); // DONT TRANSLATE
00495 //            that->d->current()->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext());
00496        }
00497        else {
00498 //            qDebug(QString("using parstyle at %1").arg(pos));
00499        }
00500        assert (that->d->current()->parstyle);
00501        return *that->d->current()->parstyle;
00502 }
00503 
00504 const ParagraphStyle& StoryText::defaultStyle() const
00505 {
00506        assert(d);
00507        return d->defaultStyle;
00508 }
00509 
00510 
00511 void StoryText::setDefaultStyle(const ParagraphStyle& style)
00512 {
00513        const StyleContext * oldPContext = d->defaultStyle.context();
00514 //     const StyleContext * oldCContext = d->defaultStyle.charStyle().context();
00515        d->defaultStyle = style;
00516        d->defaultStyle.setContext( oldPContext );
00517 //     d->defaultStyle.setName( "storydefault" ); // DONT TRANSLATE
00518 //     d->defaultStyle.charStyle().setName( "cstorydefault" ); // DONT TRANSLATE
00519 //     qDebug(QString("defstyle %1 context %2 defcstyle %3 ccontext %4 newcontext %5")
00520 //               .arg((uint)&d->defaultStyle,16).arg((uint)oldPContext,16)
00521 //               .arg((uint)&d->defaultStyle.charStyle(),16).arg((uint)oldCContext,16)
00522 //               .arg((uint)d->defaultStyle.charStyle().context(),16));
00523 //     d->defaultStyle.charStyle().setContext( oldCContext );
00524        invalidateAll();
00525 }
00526 
00527 
00528 void StoryText::applyCharStyle(int pos, uint len, const CharStyle& style )
00529 {
00530        if (pos < 0)
00531               pos += length();
00532 
00533        assert(pos >= 0);
00534        assert(pos + signed(len) <= length());
00535 
00536        if (len == 0)
00537               return;
00538 
00539        d->at(pos);
00540        for (uint i=pos; i < pos+len; ++i) {
00541               if (d->current()->ch[0] == SpecialChars::PARSEP && d->current()->parstyle != NULL)
00542                      d->current()->parstyle->charStyle().applyCharStyle(style);
00543               d->current()->applyCharStyle(style);
00544               d->next();
00545        }
00546 
00547        invalidate(pos, pos + len);
00548 }
00549 
00550 
00551 
00552 void StoryText::eraseCharStyle(int pos, uint len, const CharStyle& style )
00553 {
00554        if (pos < 0)
00555               pos += length();
00556        
00557        assert(pos >= 0);
00558        assert(pos + signed(len) <= length());
00559        
00560        if (len == 0)
00561               return;
00562        
00563        d->at(pos);
00564        for (uint i=pos; i < pos+len; ++i) {
00565               if (d->current()->ch[0] == SpecialChars::PARSEP && d->current()->parstyle != NULL)
00566                      d->current()->parstyle->charStyle().eraseCharStyle(style);
00567               d->current()->eraseCharStyle(style);
00568               d->next();
00569        }
00570        
00571        invalidate(pos, pos + len);
00572 }
00573 
00574 
00575 void StoryText::applyStyle(int pos, const ParagraphStyle& style)
00576 {
00577        if (pos < 0)
00578               pos += length();
00579 
00580        assert(pos >= 0);
00581        assert(pos <= length());
00582 
00583        int i = pos;
00584        while (i < length() && d->at(i)->ch[0] != SpecialChars::PARSEP) {
00585               ++i;
00586        }
00587        if (i < length()) {
00588               if (!d->at(i)->parstyle) {
00589                      qDebug(QString("PARSEP without style at pos %1").arg(i));
00590                      d->at(i)->parstyle = new ParagraphStyle();
00591                      d->at(i)->parstyle->setContext( & d->pstyleContext);
00592 //                   d->at(i)->parstyle->setName( "para(applyStyle)" ); // DONT TRANSLATE
00593 //                   d->at(i)->parstyle->charStyle().setName( "cpara(applyStyle)" ); // DONT TRANSLATE
00594 //                   d->at(i)->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext() );
00595               }
00596 //            qDebug(QString("applying parstyle %2 at %1 for %3").arg(i).arg(paragraphStyle(pos).name()).arg(pos));
00597               d->at(i)->parstyle->applyStyle(style);
00598        }
00599        else {
00600               // not happy about this but inserting a new PARSEP makes more trouble
00601 //            qDebug(QString("applying parstyle %1 as defaultstyle for %2").arg(paragraphStyle(pos).name()).arg(pos));
00602               d->trailingStyle.applyStyle(style);
00603        }
00604        invalidate(pos, QMIN(i, length()));
00605 }
00606 
00607 void StoryText::eraseStyle(int pos, const ParagraphStyle& style)
00608 {
00609        if (pos < 0)
00610               pos += length();
00611        
00612        assert(pos >= 0);
00613        assert(pos <= length());
00614               
00615        int i = pos;
00616        while (i < length() && d->at(i)->ch[0] != SpecialChars::PARSEP) {
00617               ++i;
00618        }
00619        if (i < length()) {
00620               if (!d->at(i)->parstyle) {
00621                      qDebug(QString("PARSEP without style at pos %1").arg(i));
00622                      d->at(i)->parstyle = new ParagraphStyle();
00623                      d->at(i)->parstyle->setContext( & d->pstyleContext);
00624 //                   d->at(i)->parstyle->setName( "para(eraseStyle)" ); // DONT TRANSLATE
00625 //                   d->at(i)->parstyle->charStyle().setName( "cpara(eraseStyle)" ); // DONT TRANSLATE
00626 //                   d->at(i)->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext());
00627               }
00628               //            qDebug(QString("applying parstyle %2 at %1 for %3").arg(i).arg(paragraphStyle(pos).name()).arg(pos));
00629               d->at(i)->parstyle->eraseStyle(style);
00630        }
00631        else {
00632               // not happy about this but inserting a new PARSEP makes more trouble
00633               //            qDebug(QString("applying parstyle %1 as defaultstyle for %2").arg(paragraphStyle(pos).name()).arg(pos));
00634               d->trailingStyle.eraseStyle(style);
00635        }
00636        invalidate(pos, QMIN(i, length()));
00637 }
00638 
00639 
00640 void StoryText::setStyle(int pos, const ParagraphStyle& style)
00641 {
00642        eraseStyle(pos, paragraphStyle(pos));
00643        applyStyle(pos, style);
00644 }
00645 
00646 
00647 void StoryText::setCharStyle(int pos, uint len, const CharStyle& style)
00648 {
00649        if (pos < 0)
00650               pos += length();
00651        
00652        assert(pos >= 0);
00653        assert(len <= unsigned(length()));
00654        assert(pos + signed(len) <= length());
00655        
00656        if (len == 0)
00657               return;
00658        
00659        d->at(pos);
00660        for (uint i=pos; i < pos+len; ++i) {
00661               if (d->current()->ch[0] == SpecialChars::PARSEP && d->current()->parstyle != NULL)
00662                      d->current()->parstyle->charStyle() = style;
00663               d->current()->setStyle(style);
00664               d->next();
00665        }
00666        
00667        invalidate(pos, pos + len);
00668 }
00669 
00670 
00671 
00672 void StoryText::getNamedResources(ResourceCollection& lists) const
00673 {
00674        d->defaultStyle.getNamedResources(lists);
00675        d->trailingStyle.getNamedResources(lists);
00676 
00677        for (int i=0; i < length(); ++i)
00678        {
00679               if (text(i) == SpecialChars::PARSEP)
00680                      paragraphStyle(i).getNamedResources(lists);
00681               else if (text(i) == SpecialChars::OBJECT)
00682                      object(i)->getNamedResources(lists);
00683               else
00684                      charStyle(i).getNamedResources(lists);
00685        }
00686 }
00687 
00688 
00689 void StoryText::replaceStyles(QMap<QString,QString> newNameForOld)
00690 {
00691        ResourceCollection newnames;
00692        newnames.mapStyles(newNameForOld);
00693        replaceNamedResources(newnames);
00694 }
00695 
00696 void StoryText::replaceNamedResources(ResourceCollection& newNames)
00697 {
00698        int len = length();
00699 
00700        d->defaultStyle.replaceNamedResources(newNames);
00701        d->trailingStyle.replaceNamedResources(newNames);
00702        
00703        if (len == 0)
00704               return;
00705        
00706        d->at(0);
00707        for (int i=0; i < len; ++i) {
00708               if (d->current()->parstyle)
00709                      d->current()->parstyle->replaceNamedResources(newNames);
00710               else
00711                      d->current()->replaceNamedResources(newNames);
00712               d->next();
00713        }
00714        
00715        invalidate(0, len);  
00716 }
00717 
00718 
00719 void StoryText::replaceCharStyles(QMap<QString,QString> newNameForOld)
00720 {
00721        ResourceCollection newnames;
00722        newnames.mapCharStyles(newNameForOld);
00723        replaceNamedResources(newnames);
00724 }
00725 
00726 
00727 uint StoryText::nrOfParagraphs() const
00728 {
00729        uint result = 0;
00730        StoryText* that = const_cast<StoryText *>(this);
00731        that->d->at(0);
00732        bool lastWasPARSEP = true;
00733        for (int i=0; i < length(); ++i) {
00734               lastWasPARSEP = that->d->current()->ch[0] == SpecialChars::PARSEP;
00735               if (lastWasPARSEP)
00736                      ++result;
00737               that->d->next();
00738        }
00739        return lastWasPARSEP? result : result + 1;
00740 }
00741 
00742 int StoryText::startOfParagraph(uint index) const
00743 {
00744        if (index == 0)
00745               return 0;
00746 
00747        StoryText* that = const_cast<StoryText *>(this);
00748        that->d->at(0);
00749        for (int i=0; i < length(); ++i) {
00750               if (that->d->current()->ch[0] == SpecialChars::PARSEP && ! --index)
00751                      return i + 1;
00752               that->d->next();
00753        }
00754        return length();
00755 }
00756 
00757 int StoryText::endOfParagraph(uint index) const
00758 {
00759        ++index;
00760        StoryText* that = const_cast<StoryText *>(this);
00761        that->d->at(0);
00762        for (int i=0; i < length(); ++i) {
00763               if (that->d->current()->ch[0] == SpecialChars::PARSEP && ! --index)
00764                      return i;
00765               that->d->next();
00766        }
00767        return length();
00768 }
00769 
00770 uint StoryText::nrOfRuns() const
00771 {
00772        return length();
00773 }
00774 
00775 int StoryText::startOfRun(uint index) const
00776 {
00777        return index;
00778 }
00779 
00780 int StoryText::endOfRun(uint index) const
00781 {
00782        return index + 1;
00783 }
00784 
00785 // positioning. all positioning methods return char positions
00786 // FIXME: make that methods use correct semantic boundaries
00787 
00788 static QString wordBoundaries(" .,:;\"'!?\n");
00789 static QString sentenceBoundaries(".:!?\n");
00790 
00791 int StoryText::nextChar(int pos)
00792 {
00793        if (pos < length())
00794               return pos+1;
00795        else
00796               return length();
00797 }
00798 int StoryText::prevChar(int pos)
00799 {
00800        if (pos > 0)
00801               return pos - 1;
00802        else 
00803               return 0;
00804 }
00805 int StoryText::nextWord(int pos)
00806 {
00807        int len = length();
00808        pos = QMIN(len, pos+1);
00809        while (pos < len  && wordBoundaries.find(text(pos)) < 0)
00810               ++pos;
00811        return pos < len ? pos + 1 : pos;
00812 }
00813 int StoryText::prevWord(int pos)
00814 {
00815        pos = QMAX(0, pos-1);
00816        while (pos > 0 && wordBoundaries.find(text(pos)) < 0)
00817               --pos;
00818        return wordBoundaries.find(text(pos)) < 0 ? pos + 1 : pos;
00819 }
00820 int StoryText::nextSentence(int pos)
00821 {
00822        int len = length();
00823        pos = QMIN(len, pos+1);
00824        while (pos < len && sentenceBoundaries.find(text(pos)) < 0)
00825               ++pos;
00826        return pos < len ? pos + 1 : pos;
00827 }
00828 int StoryText::prevSentence(int pos)
00829 {
00830        pos = QMAX(0, pos-1);
00831        while (pos > 0 && sentenceBoundaries.find(text(pos)) < 0)
00832               --pos;
00833        return sentenceBoundaries.find(text(pos)) < 0 ? pos + 1 : pos;
00834 }
00835 int StoryText::nextParagraph(int pos)
00836 {
00837        int len = length();
00838        pos = QMIN(len, pos+1);
00839        while (pos < len && text(pos) != SpecialChars::PARSEP)
00840               ++pos;
00841        return pos;
00842 }
00843 int StoryText::prevParagraph(int pos)
00844 {
00845        pos = QMAX(0, pos-1);
00846        while (pos > 0 && text(pos) != SpecialChars::PARSEP)
00847               --pos;
00848        return pos;
00849 }
00850 
00851 // these need valid layout:
00852 
00853 int StoryText::startOfLine(int pos)
00854 {
00855        for (uint i=0; i < m_lines.count(); ++i) {
00856               const LineSpec & ls(m_lines.at(i));
00857               if (ls.firstItem <= pos && pos <= ls.lastItem)
00858                      return ls.firstItem;
00859        }
00860        return 0;
00861 }
00862 int StoryText::endOfLine(int pos)
00863 {
00864        for (uint i=0; i < m_lines.count(); ++i) {
00865               const LineSpec & ls(m_lines.at(i));
00866               if (ls.firstItem <= pos && pos <= ls.lastItem)
00867                      return text(ls.lastItem) == SpecialChars::PARSEP ? ls.lastItem : 
00868                             text(ls.lastItem) == ' ' ? ls.lastItem : ls.lastItem + 1;
00869        }
00870        return length();
00871 }
00872 int StoryText::prevLine(int pos)
00873 {
00874        for (uint i=0; i < m_lines.count(); ++i) 
00875        {
00876               // find line for pos
00877               const LineSpec & ls(m_lines.at(i));
00878               if (ls.firstItem <= pos && pos <= ls.lastItem) 
00879               {
00880                      if (i == 0)
00881                             return startOfLine(pos);
00882                      // find current xpos
00883                      double xpos = 0.0;
00884                      for (int j = ls.firstItem; j < pos; ++j)
00885                             xpos += item(j)->glyph.wide();
00886                      if (pos != m_lastMagicPos || xpos > m_magicX)
00887                             m_magicX = xpos;
00888                      const LineSpec & ls2(m_lines.at(i-1));
00889                      // find new cpos
00890                      xpos = 0.0;
00891                      for (int j = ls2.firstItem; j <= ls2.lastItem; ++j) 
00892                      {
00893                             xpos += item(j)->glyph.wide();
00894                             if (xpos > m_magicX) {
00895                                    m_lastMagicPos = j;
00896                                    return j;
00897                             }
00898                      }
00899                      m_lastMagicPos = ls2.lastItem;
00900                      return ls2.lastItem;
00901               }
00902        }
00903        return firstFrameItem;
00904 }
00905 
00906 int StoryText::nextLine(int pos)
00907 {
00908        for (uint i=0; i < m_lines.count(); ++i) 
00909        {
00910               // find line for pos
00911               const LineSpec & ls(m_lines.at(i));
00912               if (ls.firstItem <= pos && pos <= ls.lastItem) 
00913               {
00914                      if (i+1 == m_lines.count())
00915                             return endOfLine(pos);
00916                      // find current xpos
00917                      double xpos = 0.0;
00918                      for (int j = ls.firstItem; j < pos; ++j)
00919                             xpos += item(j)->glyph.wide();
00920                      if (pos != m_lastMagicPos || xpos > m_magicX)
00921                             m_magicX = xpos;
00922                      const LineSpec & ls2(m_lines.at(i+1));
00923                      // find new cpos
00924                      xpos = 0.0;
00925                      for (int j = ls2.firstItem; j <= ls2.lastItem; ++j) 
00926                      {
00927                             xpos += item(j)->glyph.wide();
00928                             if (xpos > m_magicX) {
00929                                    m_lastMagicPos = j;
00930                                    return j;
00931                             }
00932                      }
00933                      m_lastMagicPos = ls2.lastItem + 1;
00934                      return ls2.lastItem + 1;
00935               }
00936        }
00937        return lastFrameItem;
00938 }
00939 
00940 int StoryText::startOfFrame(int /*pos*/) 
00941 {
00942        return firstFrameItem;
00943 }
00944 
00945 int StoryText::endOfFrame(int /*pos*/)
00946 {
00947        return lastFrameItem + 1;
00948 }
00949 
00950 // selection
00951 
00952 int StoryText::startOfSelection() const
00953 {
00954        return selFirst <= selLast? selFirst : 0;
00955 }
00956 
00957 int StoryText::endOfSelection() const
00958 {
00959        return selFirst <= selLast? selLast + 1 : -1;
00960 }
00961 
00962 int StoryText::lengthOfSelection() const
00963 {
00964        return selFirst <= selLast? selLast - selFirst + 1 : 0;
00965 }
00966 
00967 
00968 bool StoryText::selected(int pos) const
00969 {
00970        return (selFirst <= pos && pos <= selLast) 
00971 //            || (pos >= 0 && pos < length() && const_cast<StoryText*>(this)->d->at(pos)->cselect)
00972        ;
00973 }
00974 
00975 void StoryText::select(int pos, uint len, bool on)
00976 {
00977        if (pos < 0)
00978               pos += length();
00979 
00980        assert( pos >= 0 );
00981        assert( pos + signed(len) <= length() );
00982 
00983 //     qDebug("old selection: %d - %d", selFirst, selLast);
00984 
00985 //     StoryText* that = const_cast<StoryText *>(this);
00986 //     for (int i=pos; i < pos+signed(len); ++i)
00987 //            that->at(i)->cselect = on;
00988 
00989        if (on) {
00990               // extend if possible
00991               if (selected(pos - 1))
00992                      selLast = QMAX(selLast, pos + static_cast<int>(len) - 1);
00993               else if (selected(pos + len))
00994                      selFirst = QMIN(selFirst, pos);
00995               else {
00996                      selFirst = pos;
00997                      selLast = pos + len - 1;
00998               }
00999        }
01000        else {
01001               if (pos <= selFirst && selLast < pos + signed(len))
01002                      deselectAll();
01003               // shrink
01004               else if (!selected(pos - 1) && selected(pos + len - 1))
01005                      selFirst = pos + len;
01006               else if (selected(pos) && !selected(pos + len))
01007                      selLast = pos - 1;
01008               else if (selected(pos) || selected(pos + len - 1))
01009                      // Grr, deselection splits selection
01010                      selLast = pos - 1;
01011        }
01012        
01013 //     qDebug("new selection: %d - %d", selFirst, selLast);
01014 }
01015 
01016 void StoryText::selectAll()
01017 {
01018 /*     StoryText* that = const_cast<StoryText *>(this);
01019        that->at(0);
01020        for (int i=0; i < length(); ++i) {
01021               that->current()->cselect = true;
01022               that->next();
01023        }
01024 */
01025        selFirst = 0;
01026        selLast = length() - 1;
01027 }
01028 
01029 void StoryText::deselectAll()
01030 {
01031 /*     StoryText* that = const_cast<StoryText *>(this);
01032        that->at(0);
01033        for (int i=0; i < length(); ++i) {
01034               that->current()->cselect = false;
01035               that->next();
01036        }
01037 */     
01038        selFirst = 0;
01039        selLast = -1;
01040 }
01041 
01042 void StoryText::removeSelection()
01043 {
01044 //     qDebug("removeSelection: %d - %d", selFirst, selLast);
01045        if (selFirst > selLast)
01046               return;
01047 
01048        assert( selFirst >= 0 );
01049        assert( selLast < length() );
01050 
01051        removeChars(selFirst, selLast - selFirst+1);
01052        deselectAll();
01053 }
01054 
01055 
01056 
01057 void StoryText::invalidateObject(const PageItem * /*embedded*/)
01058 {
01059 }
01060 
01061 void StoryText::invalidateLayout()
01062 {
01063 }
01064 
01065 void StoryText::invalidateAll()
01066 {
01067        d->pstyleContext.invalidate();
01068        invalidate(0, nrOfItems());
01069 }
01070 
01071 void StoryText::invalidate(int firstItem, int endItem)
01072 {
01073        for (int i=firstItem; i < endItem; ++i) {
01074               ParagraphStyle* par = item(i)->parstyle;
01075               if (par)
01076                      par->charStyleContext()->invalidate();
01077        }
01078 }
01079 
01080 
01081 // physical view
01082 
01083 /*
01084 void StoryText::validate()
01085 {
01086        static bool withinValidate = false;
01087        
01088        assert( !withinValidate );
01089        withinValidate = true;
01090        
01091        withinValidate = false;
01092 }
01093 */
01094 
01095 int StoryText::screenToPosition(FPoint coord) const
01096 {
01097        double maxx = coord.x() - 1.0;
01098        for (unsigned int i=0; i < lines(); ++i)
01099        {
01100               LineSpec ls = line(i);
01101 //            qDebug(QString("screenToPosition: (%1,%2) -> y %3 - %4 + %5").arg(coord.x()).arg(coord.y()).arg(ls.y).arg(ls.ascent).arg(ls.descent));
01102               if (ls.y + ls.descent < coord.y())
01103                      continue;
01104               double xpos = ls.x;
01105               for (int j = ls.firstItem; j <= ls.lastItem; ++j) {
01106 //                          qDebug(QString("screenToPosition: (%1,%2) -> x %3 + %4").arg(coord.x()).arg(coord.y()).arg(xpos).arg(item(j)->glyph.wide()));
01107                      double width = item(j)->glyph.wide();
01108                      xpos += width;
01109                      if (xpos >= coord.x())
01110                             return xpos - width/2 > coord.x() ? j : j+1;
01111               }
01112               if (xpos > maxx)
01113                      maxx = xpos;
01114               if (xpos + 1.0 > coord.x()) // allow 1pt after end of line
01115                      return ls.lastItem + 1;
01116               else if (xpos < ls.x + 0.01 && maxx >= coord.x()) // check for empty line
01117                      return ls.firstItem;
01118        }
01119        return QMAX(lastFrameItem+1, firstFrameItem);
01120 }
01121 
01122 
01123 FRect StoryText::boundingBox(int pos, uint len) const
01124 {
01125        FRect result;
01126        LineSpec ls;
01127        for (uint i=0; i < lines(); ++i)
01128        {
01129               ls = line(i);
01130               if (ls.lastItem < pos)
01131                      continue;
01132               if (ls.firstItem <= pos) {
01133                      /*
01134                      if (ls.lastItem == pos && (item(pos)->effects() & ScStyle_SuppressSpace)  )
01135                      {
01136                             if (i+1 < lines())
01137                             {
01138                                    ls = line(i+1);
01139                                    result.setRect(ls.x, ls.y - ls.ascent, 1, ls.ascent + ls.descent);
01140                             }
01141                             else
01142                             {
01143                                    ls = line(lines()-1);
01144                                    const ParagraphStyle& pstyle(paragraphStyle(pos));
01145                                    result.setRect(ls.x, ls.y + pstyle.lineSpacing() - ls.ascent, 1, ls.ascent + ls.descent);
01146                             }
01147                      }
01148                      else */
01149                      {
01150                             double xpos = ls.x;
01151                             for (int j = ls.firstItem; j < pos; ++j)
01152                                    xpos += item(j)->glyph.wide();
01153                             result.setRect(xpos, ls.y - ls.ascent, pos < length()? item(pos)->glyph.wide() : 1, ls.ascent + ls.descent);
01154                      }
01155                      return result;
01156               }
01157        }
01158 
01159        const ParagraphStyle& pstyle(paragraphStyle(pos));
01160        if (lines() > 0)
01161        {
01162               ls = line(lines()-1);              
01163               result.setRect(ls.x, ls.y + pstyle.lineSpacing() - ls.ascent, 1, ls.ascent + ls.descent);
01164        }
01165        else
01166        {
01167               result.setRect(1, 1, 1, pstyle.lineSpacing());
01168        }      
01169        return result;
01170 }
01171 
01172 
01173 int StoryText::layout(int startItem)
01174 {
01175        //FIXME:NLS
01176        return -1;
01177 }
01178 
01179 
01180 uint StoryText::nrOfItems() const
01181 {
01182        return length();
01183 }
01184 
01185 
01186 ScText*  StoryText::item(uint itm)
01187 {
01188        assert( static_cast<int>(itm) < length() );
01189        return const_cast<StoryText *>(this)->d->at(itm);
01190 }
01191 
01192 
01193 const ScText*  StoryText::item(uint itm) const
01194 {
01195        assert( static_cast<int>(itm) < length() );
01196        return const_cast<StoryText *>(this)->d->at(itm);
01197 }
01198 
01199 
01200 const QString StoryText::itemText(uint itm) const
01201 {
01202        
01203        assert( static_cast<int>(itm) < length() );
01204 
01205        return text(itm, 1);
01206 }
01207 
01208 
01209 const CharStyle StoryText::itemStyle(uint itm) const
01210 {
01211        assert( static_cast<int>(itm) < length() );
01212 
01213        return charStyle(itm);
01214 }
01215        
01216 
01217 int StoryText::startOfItem(uint itm) const
01218 {
01219        assert( static_cast<int>(itm) < length() );
01220 
01221        return itm;
01222 }
01223 
01224 int StoryText::endOfItem(uint itm) const
01225 {
01226        assert( static_cast<int>(itm) < length() );
01227 
01228        return itm + 1;
01229 }
01230 
01231 
01232 using namespace desaxe;
01233 
01234 void StoryText::saxx(SaxHandler& handler, const Xml_string& elemtag) const
01235 {
01236        Xml_attr empty;
01237        Xml_attr pageno;
01238        pageno.insert("name", "pgno");
01239 
01240        handler.begin(elemtag, empty);
01241        defaultStyle().saxx(handler, "defaultstyle");
01242 
01243        CharStyle lastStyle(charStyle(0));
01244        bool lastWasPar = true;
01245        int lastPos = 0;
01246        handler.begin("p", empty);
01247        paragraphStyle(0).saxx(handler);
01248        handler.begin("span", empty);
01249        lastStyle.saxx(handler);
01250        for (int i=0; i < length(); ++i)
01251        {
01252               const QChar curr(text(i));
01253               const CharStyle& style(charStyle(i));
01254               
01255               if (curr == SpecialChars::OBJECT ||
01256                      curr == SpecialChars::TAB ||
01257                      curr == SpecialChars::PARSEP ||
01258                      curr == SpecialChars::LINEBREAK ||
01259                      curr == SpecialChars::COLBREAK ||
01260                      curr == SpecialChars::FRAMEBREAK ||
01261                      curr == SpecialChars::PAGENUMBER ||
01262                      curr.unicode() < 32 || 
01263                      (0xd800 <= curr.unicode() && curr.unicode() < 0xe000) ||
01264                      curr.unicode() == 0xfffe || curr.unicode() == 0xffff ||
01265                      style != lastStyle)
01266               {
01267                      // something new, write pending chars
01268                      if  (i - lastPos > 0)
01269                      {
01270                             handler.chars(text(lastPos, i-lastPos));
01271                      }
01272                      lastPos = i;
01273               }
01274 
01275               lastWasPar = (curr == SpecialChars::PARSEP);
01276               if (lastWasPar)
01277               {
01278                      handler.end("span");
01279                      handler.end("p");
01280                      handler.begin("p", empty);
01281                      paragraphStyle(i+1).saxx(handler);
01282                      handler.begin("span", empty);
01283               }
01284               else if (curr == SpecialChars::OBJECT && object(i) != NULL)
01285               {
01286                      object(i)->saxx(handler);
01287               }
01288               else if (curr == SpecialChars::TAB)
01289               {
01290                      handler.beginEnd("tab", empty);
01291               }
01292               else if (curr == SpecialChars::LINEBREAK)
01293               {
01294                      handler.beginEnd("breakline", empty);
01295               }
01296               else if (curr == SpecialChars::COLBREAK)
01297               {
01298                      handler.beginEnd("breakcol", empty);
01299               }
01300               else if (curr == SpecialChars::FRAMEBREAK)
01301               {
01302                      handler.beginEnd("breakframe", empty);
01303               }
01304               else if (curr == SpecialChars::PAGENUMBER)
01305               {
01306                      handler.beginEnd("var", pageno);
01307               }
01308               else if (curr.unicode() < 32 || 
01309                              (0xd800 <= curr.unicode() && curr.unicode() < 0xe000) ||
01310                              curr.unicode() == 0xfffe || curr.unicode() == 0xffff)
01311               {
01312                      Xml_attr unic;
01313                      unic.insert("code", toXMLString(curr.unicode()));
01314                      handler.beginEnd("unicode", unic);
01315               }
01316               else if (lastStyle != style)
01317               {
01318                      handler.end("span");
01319                      handler.begin("span", empty);
01320                      style.saxx(handler);
01321                      lastStyle = style;
01322                      continue;
01323               }
01324               else
01325                      continue;
01326               lastPos = i+1;
01327        }
01328        
01329        if  (length() - lastPos > 0)
01330               handler.chars(text(lastPos, length()-lastPos));
01331        handler.end("span");
01332        handler.end("p");
01333        
01334 //     if (!lastWasPar)
01335 //            paragraphStyle(length()-1).saxx(handler);
01336        
01337        handler.end(elemtag);
01338 
01339 }
01340 
01341 
01342 class AppendText_body : public Action_body
01343 {
01344 public:       
01345        void chars(const Xml_string& txt)
01346        {
01347               StoryText* obj = this->dig->top<StoryText>();
01348               obj->insertChars(-1, txt ); 
01349        }
01350 };
01351 
01352 struct  AppendText : public MakeAction<AppendText_body> 
01353 {};
01354 
01355 
01356 class AppendSpecial_body : public Action_body
01357 {
01358 public:
01359        AppendSpecial_body(QChar sp) : chr(sp) {}
01360        
01361        void begin(const Xml_string& tag, Xml_attr attr)
01362        {
01363               StoryText* obj = this->dig->top<StoryText>();
01364               Xml_attr::iterator code = attr.find("code");
01365               if (tag == "unicode" && code != attr.end())
01366                      obj->insertChars(-1, QChar(parseUInt(Xml_data(code))));
01367               else
01368                      obj->insertChars(-1, chr);
01369        }
01370 private:
01371        QChar chr;
01372 };
01373 
01374 struct AppendSpecial : public MakeAction<AppendSpecial_body, QChar>
01375 {
01376        AppendSpecial(QChar sp) : MakeAction<AppendSpecial_body, QChar>(sp) {}
01377        AppendSpecial() : MakeAction<AppendSpecial_body, QChar>(SpecialChars::BLANK) {}
01378 };
01379 
01380 
01381 class AppendInlineFrame_body : public Action_body
01382 {
01383 public:
01384        void end(const Xml_string&) // this could be a setter if we had StoryText::appendObject() ...
01385        {
01386               StoryText* story = this->dig->top<StoryText>(1);
01387               PageItem* obj = this->dig->top<PageItem>(0);
01388               story->insertObject(-1, obj);
01389        }
01390 };
01391 
01392 struct AppendInlineFrame : public MakeAction<AppendInlineFrame_body>
01393 {};
01394 
01395 /*
01396 class ApplyStyle_body : public Action_body
01397 {
01398 public:
01399        void end(const Xml_string& tag) 
01400        {
01401               qDebug("storytext desaxe: apply style");
01402               StoryText* story = this->dig->top<StoryText>(1);
01403               ParagraphStyle* obj = this->dig->top<ParagraphStyle>(0);
01404               story->applyStyle(-1, *obj);
01405        }
01406 };
01407 
01408 struct ApplyStyle : public MakeAction<ApplyStyle_body>
01409 {};
01410 
01411 
01412 class ApplyCharStyle_body : public Action_body
01413 {
01414 public:
01415        ApplyCharStyle_body() : storyTag(StoryText::saxxDefaultElem), lastPos(0), lastStyle()
01416        {}
01417        ApplyCharStyle_body(const Xml_string& tag) : storyTag(tag), lastPos(0), lastStyle()
01418        {}
01419        
01420        void end(const Xml_string& tag) 
01421        {
01422               qDebug("storytext desaxe: apply charstyle");
01423               if (tag == CharStyle::saxxDefaultElem)
01424               {
01425                      StoryText* story = this->dig->top<StoryText>(1);
01426                      CharStyle* obj = this->dig->top<CharStyle>(0);
01427                      int len = story->length();
01428                      if (len > lastPos && lastStyle != *obj)
01429                      {
01430                             story->applyCharStyle(lastPos, len - lastPos, lastStyle);
01431                             lastPos = len;
01432                             lastStyle = *obj;
01433                      }
01434               }
01435               else if (tag == StoryText::saxxDefaultElem)
01436               {
01437                      StoryText* story = this->dig->top<StoryText>();
01438                      int len = story->length();
01439                      if (len > lastPos)
01440                      {
01441                             story->applyCharStyle(lastPos, len - lastPos, lastStyle);
01442                      }
01443               }
01444        }
01445 private:
01446        Xml_string storyTag;
01447        int lastPos;
01448        CharStyle lastStyle;
01449 };
01450 
01451 struct ApplyCharStyle : public MakeAction<ApplyCharStyle_body, const Xml_string&>
01452 {
01453        ApplyCharStyle() : MakeAction<ApplyCharStyle_body, const Xml_string&>() {}
01454        ApplyCharStyle(const Xml_string& tag) : MakeAction<ApplyCharStyle_body, const Xml_string&>(tag) {}
01455 };
01456 
01457 */
01458 
01459 class Paragraph_body : public Action_body
01460 {
01461 public:
01462        Paragraph_body() : lastPos(0), lastStyle(NULL)
01463        {}
01464        
01465        ~Paragraph_body() 
01466        {
01467               if (lastStyle)
01468                      delete lastStyle;
01469        }
01470        
01471        void begin(const Xml_string& tag, Xml_attr)
01472        {
01473               if (tag == "p")
01474               {
01475                      StoryText* story = this->dig->top<StoryText>();
01476 //                   qDebug(QString("startpar: %1->%2 %3->NULL").arg(lastPos).arg(story->length()).arg((ulong)lastStyle));
01477                      lastPos = story->length();
01478                      if (lastPos > 0) {
01479                             story->insertChars(-1, SpecialChars::PARSEP);
01480                             ++lastPos;
01481                      }
01482                      if (lastStyle)
01483                             delete lastStyle;
01484                      lastStyle = NULL;
01485               }
01486        }
01487        
01488        void end(const Xml_string& tag) 
01489        {
01490               if (tag == ParagraphStyle::saxxDefaultElem)
01491               {
01492                      if (lastStyle)
01493                             delete lastStyle;
01494                      lastStyle = this->dig->top<ParagraphStyle>(0);
01495 //                   qDebug(QString("endstyle: %1 %2 %3").arg("?").arg(lastPos).arg((ulong)lastStyle));
01496               }
01497               else if (tag == "p")
01498               {
01499                      StoryText* story = this->dig->top<StoryText>();
01500                      int len = story->length();
01501 //                   qDebug(QString("endpar: %1 %2 %3 %4").arg(len).arg(lastPos).arg((ulong)lastStyle).arg(lastStyle? lastStyle->parent() : QString()));
01502                      if (len > lastPos && lastStyle)
01503                      {
01504                             story->applyStyle(lastPos, *lastStyle);
01505                      }
01506               }
01507        }
01508 private:
01509        int lastPos;
01510        ParagraphStyle* lastStyle;
01511 };
01512 
01513 struct Paragraph : public MakeAction<Paragraph_body>
01514 {
01515        Paragraph() : MakeAction<Paragraph_body>() {}
01516 };
01517 
01518 
01519 class SpanAction_body : public Action_body
01520 {
01521 public:
01522        SpanAction_body() : lastPos(0), lastStyle(NULL)
01523        {}
01524        
01525        ~SpanAction_body() 
01526        {
01527               if (lastStyle)
01528                      delete lastStyle;
01529        }
01530        
01531        void begin(const Xml_string& tag, Xml_attr)
01532        {
01533 //            qDebug(QString("spanaction: begin %1").arg(tag));
01534               if (tag == "span")
01535               {
01536                      StoryText* story = this->dig->top<StoryText>();
01537                      lastPos = story->length();
01538                      if (lastStyle)
01539                             delete lastStyle;
01540                      lastStyle = NULL;
01541               }
01542        }
01543        
01544        void end(const Xml_string& tag) 
01545        {
01546               if (tag == CharStyle::saxxDefaultElem)
01547 //                   qDebug(QString("spanaction: end %1").arg(tag));
01548               {
01549                      if (lastStyle)
01550                             delete lastStyle;
01551                      lastStyle = this->dig->top<CharStyle>(0);
01552               }
01553               else if (tag == "span")
01554               {
01555                      StoryText* story = this->dig->top<StoryText>();
01556                      int len = story->length();
01557                      if (len > lastPos && lastStyle)
01558                      {
01559                             story->applyCharStyle(lastPos, len - lastPos, *lastStyle);
01560                      }
01561               }
01562        }
01563 private:
01564        int lastPos;
01565        CharStyle* lastStyle;
01566 };
01567 
01568 struct SpanAction : public MakeAction<SpanAction_body>
01569 {
01570        SpanAction() : MakeAction<SpanAction_body>() {}
01571 };
01572 
01573 
01574 const Xml_string StoryText::saxxDefaultElem("story");
01575 
01576 void StoryText::desaxeRules(const Xml_string& prefixPattern, Digester& ruleset, Xml_string elemtag)
01577 {
01578        Xml_string storyPrefix(Digester::concat(prefixPattern, elemtag));
01579        ruleset.addRule(storyPrefix, Factory<StoryText>());
01580 
01581        ParagraphStyle::desaxeRules(storyPrefix, ruleset, "defaultstyle");
01582        ruleset.addRule(Digester::concat(storyPrefix, "defaultstyle"), SetterWithConversion<StoryText, const ParagraphStyle&, ParagraphStyle>( & StoryText::setDefaultStyle ));
01583        
01584        Paragraph paraAction;
01585        Xml_string paraPrefix(Digester::concat(storyPrefix, "p"));
01586        ruleset.addRule(paraPrefix, paraAction ); 
01587        ParagraphStyle::desaxeRules(paraPrefix, ruleset, ParagraphStyle::saxxDefaultElem);
01588        ruleset.addRule(Digester::concat(paraPrefix, ParagraphStyle::saxxDefaultElem), paraAction ); 
01589        
01590        SpanAction spanAction;
01591        Xml_string spanPrefix(Digester::concat(paraPrefix, "span"));
01592        ruleset.addRule(spanPrefix, spanAction );
01593        CharStyle::desaxeRules(spanPrefix, ruleset, CharStyle::saxxDefaultElem);
01594        ruleset.addRule(Digester::concat(spanPrefix, CharStyle::saxxDefaultElem), spanAction );
01595               
01596        ruleset.addRule(spanPrefix, AppendText());
01597        
01598        ruleset.addRule(Digester::concat(spanPrefix, "breakline"), AppendSpecial(SpecialChars::LINEBREAK) );
01599        ruleset.addRule(Digester::concat(spanPrefix, "breakcol"), AppendSpecial(SpecialChars::COLBREAK) );
01600        ruleset.addRule(Digester::concat(spanPrefix, "breakframe"), AppendSpecial(SpecialChars::FRAMEBREAK) );
01601        ruleset.addRule(Digester::concat(spanPrefix, "tab"), AppendSpecial(SpecialChars::TAB) ); 
01602        ruleset.addRule(Digester::concat(spanPrefix, "unicode"), AppendSpecial() ); 
01603        ruleset.addRule(Digester::concat(spanPrefix, "var"), AppendSpecial(SpecialChars::PAGENUMBER) ); 
01604        
01605        //PageItem::desaxeRules(storyPrefix, ruleset); argh, that would be recursive!
01606        ruleset.addRule(Digester::concat(spanPrefix, "item"), AppendInlineFrame() ); 
01607        
01608 }