lib Library API Documentation

kotextobject.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2001-2005 David Faure <faure@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017    Boston, MA 02111-1307, USA.
00018 */
00019 
00020 #include "kotextobject.h"
00021 #include "kotextparag.h"
00022 #include "koparagcounter.h"
00023 #include "kozoomhandler.h"
00024 #include "kocommand.h"
00025 #include "kostyle.h"
00026 #include "koFontDia.h"
00027 #include "kooasiscontext.h"
00028 #include "kovariable.h"
00029 #include "koAutoFormat.h"
00030 #include <koxmlns.h>
00031 #include <kodom.h>
00032 
00033 #include <klocale.h>
00034 #include <kdebug.h>
00035 #include <kapplication.h>
00036 
00037 #include <qtimer.h>
00038 #include <qregexp.h>
00039 #include <qprogressdialog.h>
00040 
00041 #include <assert.h>
00042 
00043 //#define DEBUG_FORMATS
00044 //#define DEBUG_FORMAT_MORE
00045 
00046 const char KoTextObject::s_customItemChar = '#'; // Has to be transparent to kspell but still be saved (not space)
00047 
00048 struct KoTextObject::KoTextObjectPrivate
00049 {
00050 public:
00051     KoTextObjectPrivate() {
00052         afterFormattingEmitted = false;
00053         abortFormatting = false;
00054     }
00055     bool afterFormattingEmitted;
00056     bool abortFormatting;
00057 };
00058 
00059 KoTextObject::KoTextObject( KoZoomHandler *zh, const QFont& defaultFont,
00060                             const QString &defaultLanguage, bool hyphenation,
00061                             KoParagStyle* defaultStyle, int tabStopWidth,
00062                             QObject* parent, const char *name )
00063     : QObject( parent, name ), m_defaultStyle( defaultStyle ), undoRedoInfo( this )
00064 {
00065     textdoc = new KoTextDocument( zh, new KoTextFormatCollection( defaultFont, QColor(),defaultLanguage, hyphenation ) );
00066     if ( tabStopWidth != -1 )
00067         textdoc->setTabStops( tabStopWidth );
00068     init();
00069 }
00070 
00071 KoTextObject::KoTextObject( KoTextDocument* _textdoc, KoParagStyle* defaultStyle,
00072                             QObject* parent, const char *name )
00073  : QObject( parent, name ), m_defaultStyle( defaultStyle ), undoRedoInfo( this )
00074 {
00075     textdoc = _textdoc;
00076     init();
00077 }
00078 
00079 void KoTextObject::init()
00080 {
00081     d = new KoTextObjectPrivate;
00082     m_needsSpellCheck = true;
00083     m_protectContent = false;
00084     m_visible=true;
00085     m_availableHeight = -1;
00086     m_lastFormatted = textdoc->firstParag();
00087     m_highlightSelectionAdded = false;
00088     interval = 0;
00089     changeIntervalTimer = new QTimer( this );
00090     connect( changeIntervalTimer, SIGNAL( timeout() ),
00091              this, SLOT( doChangeInterval() ) );
00092 
00093     formatTimer = new QTimer( this );
00094     connect( formatTimer, SIGNAL( timeout() ),
00095              this, SLOT( formatMore() ) );
00096 
00097     // Apply default style to initial paragraph
00098     if ( m_lastFormatted && m_defaultStyle )
00099         m_lastFormatted->applyStyle( m_defaultStyle );
00100 
00101     connect( textdoc, SIGNAL( paragraphDeleted( KoTextParag* ) ),
00102              this, SIGNAL( paragraphDeleted( KoTextParag* ) ) );
00103     connect( textdoc, SIGNAL( paragraphDeleted( KoTextParag* ) ),
00104              this, SLOT( slotParagraphDeleted( KoTextParag* ) ) );
00105     connect( textdoc, SIGNAL( newCommand( KCommand* ) ),
00106              this, SIGNAL( newCommand( KCommand* ) ) );
00107     connect( textdoc, SIGNAL( repaintChanged() ),
00108              this, SLOT( emitRepaintChanged() ) );
00109 
00110     connect( this, SIGNAL(paragraphModified( KoTextParag*, int, int , int ) ),
00111              this, SLOT(slotParagraphModified(KoTextParag *, int, int , int)));
00112     connect( this, SIGNAL(paragraphCreated( KoTextParag* )),
00113              this, SLOT(slotParagraphCreated(KoTextParag *)));
00114 }
00115 
00116 KoTextObject::~KoTextObject()
00117 {
00118     // Avoid crash in KoTextString::clear -> accessing deleted format collection,
00119     // if ~UndoRedoInfo still has a string to clear.
00120     undoRedoInfo.clear();
00121     delete textdoc; textdoc = 0;
00122     delete d;
00123 }
00124 
00125 int KoTextObject::availableHeight() const
00126 {
00127     if ( m_availableHeight == -1 )
00128         emit const_cast<KoTextObject *>(this)->availableHeightNeeded();
00129     Q_ASSERT( m_availableHeight != -1 );
00130     return m_availableHeight;
00131 }
00132 
00133 void KoTextObject::slotParagraphModified(KoTextParag *parag, int /*ParagModifyType*/ _type, int , int)
00134 {
00135     if ( _type == ChangeFormat)
00136         return;
00137     m_needsSpellCheck = true;
00138     if (parag )
00139         parag->string()->setNeedsSpellCheck( true );
00140 }
00141 
00142 void KoTextObject::slotParagraphCreated(KoTextParag * parag)
00143 {
00144     m_needsSpellCheck = true;
00145     if (parag )
00146         parag->string()->setNeedsSpellCheck( true );
00147 }
00148 
00149 void KoTextObject::slotParagraphDeleted(KoTextParag * /*parag*/)
00150 {
00151     // ### TODO: remove from kwbgspellcheck
00152 }
00153 
00154 int KoTextObject::docFontSize( KoTextFormat * format ) const
00155 {
00156     Q_ASSERT( format );
00157     return format->pointSize();
00158 }
00159 
00160 int KoTextObject::zoomedFontSize( int docFontSize ) const
00161 {
00162     kdDebug(32500) << "KoTextObject::zoomedFontSize: docFontSize=" << docFontSize
00163               << " - in LU: " << KoTextZoomHandler::ptToLayoutUnitPt( docFontSize ) << endl;
00164     return KoTextZoomHandler::ptToLayoutUnitPt( docFontSize );
00165 }
00166 
00167 // A visitor that looks for custom items in e.g. a selection
00168 class KoHasCustomItemVisitor : public KoParagVisitor
00169 {
00170 public:
00171     KoHasCustomItemVisitor() : KoParagVisitor() { }
00172     // returns false when cancelled, i.e. an item was _found_, and true if it proceeded to the end(!)
00173     virtual bool visit( KoTextParag *parag, int start, int end )
00174     {
00175         for ( int i = start ; i < end ; ++i )
00176         {
00177             KoTextStringChar * ch = parag->at( i );
00178             if ( ch->isCustom() )
00179                 return false; // found one -> stop here
00180         }
00181         return true;
00182     }
00183 };
00184 
00185 bool KoTextObject::selectionHasCustomItems( int selectionId ) const
00186 {
00187     KoHasCustomItemVisitor visitor;
00188     bool noneFound = textdoc->visitSelection( selectionId, &visitor );
00189     return !noneFound;
00190 }
00191 
00192 void KoTextObject::slotAfterUndoRedo()
00193 {
00194     formatMore( 2 );
00195     emit repaintChanged( this );
00196     emit updateUI( true );
00197     emit showCursor();
00198     emit ensureCursorVisible();
00199 }
00200 
00201 void KoTextObject::clearUndoRedoInfo()
00202 {
00203     undoRedoInfo.clear();
00204 }
00205 
00206 
00207 void KoTextObject::checkUndoRedoInfo( KoTextCursor * cursor, UndoRedoInfo::Type t )
00208 {
00209     if ( undoRedoInfo.valid() && ( t != undoRedoInfo.type || cursor != undoRedoInfo.cursor ) ) {
00210         undoRedoInfo.clear();
00211     }
00212     undoRedoInfo.type = t;
00213     undoRedoInfo.cursor = cursor;
00214 }
00215 
00216 void KoTextObject::undo()
00217 {
00218     undoRedoInfo.clear();
00219     emit hideCursor();
00220     KoTextCursor *cursor = new KoTextCursor( textdoc ); // Kindof a dummy cursor
00221     KoTextCursor *c = textdoc->undo( cursor );
00222     if ( !c ) {
00223         delete cursor;
00224         emit showCursor();
00225         return;
00226     }
00227     // We have to set this new cursor position in all views :(
00228     // It sucks a bit for useability, but otherwise one view might still have
00229     // a cursor inside a deleted paragraph -> crash.
00230     emit setCursor( c );
00231     setLastFormattedParag( textdoc->firstParag() );
00232     delete cursor;
00233     QTimer::singleShot( 0, this, SLOT( slotAfterUndoRedo() ) );
00234 }
00235 
00236 void KoTextObject::redo()
00237 {
00238     undoRedoInfo.clear();
00239     emit hideCursor();
00240     KoTextCursor *cursor = new KoTextCursor( textdoc ); // Kindof a dummy cursor
00241     KoTextCursor *c = textdoc->redo( cursor );
00242     if ( !c ) {
00243         delete cursor;
00244         emit showCursor();
00245         return;
00246     }
00247     emit setCursor( c ); // see undo
00248     setLastFormattedParag( textdoc->firstParag() );
00249     delete cursor;
00250     QTimer::singleShot( 0, this, SLOT( slotAfterUndoRedo() ) );
00251 }
00252 
00253 KoTextObject::UndoRedoInfo::UndoRedoInfo( KoTextObject *to )
00254     : type( Invalid ), textobj(to), cursor( 0 )
00255 {
00256     text = QString::null;
00257     id = -1;
00258     index = -1;
00259     placeHolderCmd = 0L;
00260 }
00261 
00262 bool KoTextObject::UndoRedoInfo::valid() const
00263 {
00264     return text.length() > 0 && id >= 0 && index >= 0;
00265 }
00266 
00267 void KoTextObject::UndoRedoInfo::clear()
00268 {
00269     if ( valid() ) {
00270         KoTextDocument* textdoc = textobj->textDocument();
00271         switch (type) {
00272             case Insert:
00273             case Return:
00274             {
00275                 KoTextDocCommand * cmd = new KoTextInsertCommand( textdoc, id, index, text.rawData(), customItemsMap, oldParagLayouts );
00276                 textdoc->addCommand( cmd );
00277                 Q_ASSERT( placeHolderCmd );
00278                 // Inserting any custom items -> macro command, to let custom items add their command
00279                 if ( !customItemsMap.isEmpty() )
00280                 {
00281                     CustomItemsMap::Iterator it = customItemsMap.begin();
00282                     for ( ; it != customItemsMap.end(); ++it )
00283                     {
00284                         KoTextCustomItem * item = it.data();
00285                         KCommand * itemCmd = item->createCommand();
00286                         if ( itemCmd )
00287                             placeHolderCmd->addCommand( itemCmd );
00288                     }
00289                     placeHolderCmd->addCommand( new KoTextCommand( textobj, /*cmd, */QString::null ) );
00290                 }
00291                 else
00292                 {
00293                     placeHolderCmd->addCommand( new KoTextCommand( textobj, /*cmd, */QString::null ) );
00294                 }
00295             } break;
00296             case Delete:
00297             case RemoveSelected:
00298             {
00299                 KoTextDocCommand * cmd = textobj->deleteTextCommand( textdoc, id, index, text.rawData(), customItemsMap, oldParagLayouts );
00300                 textdoc->addCommand( cmd );
00301                 Q_ASSERT( placeHolderCmd );
00302                 placeHolderCmd->addCommand( new KoTextCommand( textobj, /*cmd, */QString::null ) );
00303                 // Deleting any custom items -> let them add their command
00304                 if ( !customItemsMap.isEmpty() )
00305                 {
00306                     customItemsMap.deleteAll( placeHolderCmd );
00307                 }
00308            } break;
00309             case Invalid:
00310                 break;
00311         }
00312     }
00313     type = Invalid;
00314     // Before Qt-3.2.0, this called KoTextString::clear(), which called resize(0) on the array, which _detached_. Tricky.
00315     // Since Qt-3.2.0, resize(0) doesn't detach anymore -> KoTextDocDeleteCommand calls copy() itself.
00316     text = QString::null;
00317     id = -1;
00318     index = -1;
00319     oldParagLayouts.clear();
00320     customItemsMap.clear();
00321     placeHolderCmd = 0L;
00322 }
00323 
00324 void KoTextObject::copyCharFormatting( KoTextParag *parag, int position, int index /*in text*/, bool moveCustomItems )
00325 {
00326     KoTextStringChar * ch = parag->at( position );
00327     if ( ch->format() ) {
00328         ch->format()->addRef();
00329         undoRedoInfo.text.at( index ).setFormat( ch->format() );
00330     }
00331     if ( ch->isCustom() )
00332     {
00333         kdDebug(32500) << "KoTextObject::copyCharFormatting moving custom item " << ch->customItem() << " to text's " << index << " char"  << endl;
00334         undoRedoInfo.customItemsMap.insert( index, ch->customItem() );
00335         // We copy the custom item to customItemsMap in all cases (see setFormat)
00336         // We only remove from 'ch' if moveCustomItems was specified
00337         if ( moveCustomItems )
00338             parag->removeCustomItem(position);
00339         //ch->loseCustomItem();
00340     }
00341 }
00342 
00343 // Based on QTextView::readFormats - with all code duplication moved to copyCharFormatting
00344 void KoTextObject::readFormats( KoTextCursor &c1, KoTextCursor &c2, bool copyParagLayouts, bool moveCustomItems )
00345 {
00346     //kdDebug(32500) << "KoTextObject::readFormats moveCustomItems=" << moveCustomItems << endl;
00347     int oldLen = undoRedoInfo.text.length();
00348     if ( c1.parag() == c2.parag() ) {
00349         undoRedoInfo.text += c1.parag()->string()->toString().mid( c1.index(), c2.index() - c1.index() );
00350         for ( int i = c1.index(); i < c2.index(); ++i )
00351             copyCharFormatting( c1.parag(), i, oldLen + i - c1.index(), moveCustomItems );
00352     } else {
00353         int lastIndex = oldLen;
00354         int i;
00355         //kdDebug(32500) << "KoTextObject::readFormats copying from " << c1.index() << " to " << c1.parag()->length()-1 << " into lastIndex=" << lastIndex << endl;
00356         // Replace the trailing spaces with '\n'. That char carries the formatting for the trailing space.
00357         undoRedoInfo.text += c1.parag()->string()->toString().mid( c1.index(), c1.parag()->length() - 1 - c1.index() ) + '\n';
00358         for ( i = c1.index(); i < c1.parag()->length(); ++i, ++lastIndex )
00359             copyCharFormatting( c1.parag(), i, lastIndex, moveCustomItems );
00360         //++lastIndex; // skip the '\n'.
00361         KoTextParag *p = c1.parag()->next();
00362         while ( p && p != c2.parag() ) {
00363             undoRedoInfo.text += p->string()->toString().left( p->length() - 1 ) + '\n';
00364             //kdDebug(32500) << "KoTextObject::readFormats (mid) copying from 0 to "  << p->length()-1 << " into i+" << lastIndex << endl;
00365             for ( i = 0; i < p->length(); ++i )
00366                 copyCharFormatting( p, i, i + lastIndex, moveCustomItems );
00367             lastIndex += p->length(); // + 1; // skip the '\n'
00368             //kdDebug(32500) << "KoTextObject::readFormats lastIndex now " << lastIndex << " - text is now " << undoRedoInfo.text.toString() << endl;
00369             p = p->next();
00370         }
00371         //kdDebug(32500) << "KoTextObject::readFormats copying [last] from 0 to " << c2.index() << " into i+" << lastIndex << endl;
00372         undoRedoInfo.text += c2.parag()->string()->toString().left( c2.index() );
00373         for ( i = 0; i < c2.index(); ++i )
00374             copyCharFormatting( c2.parag(), i, i + lastIndex, moveCustomItems );
00375     }
00376 
00377     if ( copyParagLayouts ) {
00378         KoTextParag *p = c1.parag();
00379         while ( p ) {
00380             undoRedoInfo.oldParagLayouts << p->paragLayout();
00381             if ( p == c2.parag() )
00382                 break;
00383             p = p->next();
00384         }
00385     }
00386 }
00387 
00388 void KoTextObject::newPlaceHolderCommand( const QString & name )
00389 {
00390     Q_ASSERT( !undoRedoInfo.placeHolderCmd );
00391     if ( undoRedoInfo.placeHolderCmd ) kdDebug(32500) << kdBacktrace();
00392     undoRedoInfo.placeHolderCmd = new KMacroCommand( name );
00393     emit newCommand( undoRedoInfo.placeHolderCmd );
00394 }
00395 
00396 void KoTextObject::storeParagUndoRedoInfo( KoTextCursor * cursor, int selectionId )
00397 {
00398     undoRedoInfo.clear();
00399     undoRedoInfo.oldParagLayouts.clear();
00400     undoRedoInfo.text = " ";
00401     undoRedoInfo.index = 1;
00402     if ( cursor && !textdoc->hasSelection( selectionId, true ) ) {
00403         KoTextParag * p = cursor->parag();
00404         undoRedoInfo.id = p->paragId();
00405         undoRedoInfo.eid = p->paragId();
00406         undoRedoInfo.oldParagLayouts << p->paragLayout();
00407     }
00408     else{
00409         Q_ASSERT( textdoc->hasSelection( selectionId, true ) );
00410         KoTextParag *start = textdoc->selectionStart( selectionId );
00411         KoTextParag *end = textdoc->selectionEnd( selectionId );
00412         undoRedoInfo.id = start->paragId();
00413         undoRedoInfo.eid = end->paragId();
00414         for ( ; start && start != end->next() ; start = start->next() )
00415         {
00416             undoRedoInfo.oldParagLayouts << start->paragLayout();
00417             //kdDebug(32500) << "KoTextObject:storeParagUndoRedoInfo storing counter " << start->paragLayout().counter.counterType << endl;
00418         }
00419     }
00420 }
00421 
00422 void KoTextObject::doKeyboardAction( KoTextCursor * cursor, KoTextFormat * & /*currentFormat*/, KeyboardAction action )
00423 {
00424     KoTextParag * parag = cursor->parag();
00425     setLastFormattedParag( parag );
00426     emit hideCursor();
00427     bool doUpdateCurrentFormat = true;
00428     switch ( action ) {
00429     case ActionDelete: {
00430         checkUndoRedoInfo( cursor, UndoRedoInfo::Delete );
00431         if ( !undoRedoInfo.valid() ) {
00432             newPlaceHolderCommand( i18n("Delete Text") );
00433             undoRedoInfo.id = parag->paragId();
00434             undoRedoInfo.index = cursor->index();
00435             undoRedoInfo.text = QString::null;
00436             undoRedoInfo.oldParagLayouts << parag->paragLayout();
00437         }
00438         if ( !cursor->atParagEnd() )
00439         {
00440             KoTextStringChar * ch = parag->at( cursor->index() );
00441             undoRedoInfo.text += ch->c;
00442             copyCharFormatting( parag, cursor->index(), undoRedoInfo.text.length()-1, true );
00443         }
00444         KoParagLayout paragLayout;
00445         if ( parag->next() )
00446             paragLayout = parag->next()->paragLayout();
00447 
00448         KoTextParag *old = cursor->parag();
00449         if ( cursor->remove() ) {
00450             if ( old != cursor->parag() && m_lastFormatted == old ) // 'old' has been deleted
00451                 m_lastFormatted = cursor->parag() ? cursor->parag()->prev() : 0;
00452             undoRedoInfo.text += "\n";
00453             undoRedoInfo.oldParagLayouts << paragLayout;
00454         } else
00455             emit paragraphModified( old, RemoveChar, cursor->index(), 1 );
00456     } break;
00457     case ActionBackspace: {
00458         // Remove counter
00459         if ( parag->counter() && parag->counter()->style() != KoParagCounter::STYLE_NONE && cursor->index() == 0 ) {
00460             // parag->decDepth(); // We don't have support for nested lists at the moment
00461                                   // (only in titles, but you don't want Backspace to move it up)
00462             KoParagCounter c;
00463             c.setDepth( parag->counter()->depth() );
00464             KCommand *cmd=setCounterCommand( cursor, c );
00465             if(cmd)
00466                 emit newCommand(cmd);
00467         }
00468         else if ( !cursor->atParagStart() )
00469         {
00470             checkUndoRedoInfo( cursor, UndoRedoInfo::Delete );
00471             if ( !undoRedoInfo.valid() ) {
00472                 newPlaceHolderCommand( i18n("Delete Text") );
00473                 undoRedoInfo.id = parag->paragId();
00474                 undoRedoInfo.index = cursor->index();
00475                 undoRedoInfo.text = QString::null;
00476                 undoRedoInfo.oldParagLayouts << parag->paragLayout();
00477             }
00478             undoRedoInfo.text.insert( 0, cursor->parag()->at( cursor->index()-1 ) );
00479             copyCharFormatting( cursor->parag(), cursor->index()-1, 0, true );
00480             undoRedoInfo.index = cursor->index()-1;
00481             //KoParagLayout paragLayout = cursor->parag()->paragLayout();
00482             cursor->removePreviousChar();
00483             emit paragraphModified( cursor->parag(), RemoveChar, cursor->index(),1 );
00484             m_lastFormatted = cursor->parag();
00485         } else if ( parag->prev() ) { // joining paragraphs
00486             emit paragraphDeleted( cursor->parag() );
00487             clearUndoRedoInfo();
00488         textdoc->setSelectionStart( KoTextDocument::Temp, cursor );
00489             cursor->gotoPreviousLetter();
00490         textdoc->setSelectionEnd( KoTextDocument::Temp, cursor );
00491         removeSelectedText( cursor, KoTextDocument::Temp, i18n( "Delete Text" ) );
00492             emit paragraphModified( cursor->parag(), AddChar, cursor->index(), cursor->parag()->length() - cursor->index() );
00493         }
00494     } break;
00495     case ActionReturn: {
00496         checkUndoRedoInfo( cursor, UndoRedoInfo::Return );
00497         if ( !undoRedoInfo.valid() ) {
00498             newPlaceHolderCommand( i18n("Insert Text") );
00499             undoRedoInfo.id = cursor->parag()->paragId();
00500             undoRedoInfo.index = cursor->index();
00501             undoRedoInfo.text = QString::null;
00502         }
00503         undoRedoInfo.text += "\n";
00504         if ( cursor->parag() )
00505         {
00506                 QString last_line = cursor->parag()->toString();
00507                 last_line.remove(0,last_line.find(' ')+1);
00508 
00509                 if( last_line.isEmpty() && cursor->parag()->counter() && cursor->parag()->counter()->numbering() == KoParagCounter::NUM_LIST ) //if the previous line the in paragraph is empty
00510                 {
00511                         KoParagCounter c;
00512                         KCommand *cmd=setCounterCommand( cursor, c );
00513                         if(cmd)
00514                                 emit newCommand(cmd);
00515                         setLastFormattedParag( cursor->parag() );
00516                         cursor->parag()->setNoCounter();
00517 
00518                         formatMore( 2 );
00519                         emit repaintChanged( this );
00520                         emit ensureCursorVisible();
00521                         emit showCursor();
00522                         emit updateUI( doUpdateCurrentFormat );
00523                         return;
00524                 }
00525                 else
00526                         cursor->splitAndInsertEmptyParag();
00527         }
00528 
00529         Q_ASSERT( cursor->parag()->prev() );
00530         setLastFormattedParag( cursor->parag() );
00531 
00532         doUpdateCurrentFormat = false;
00533         KoParagStyle * style = cursor->parag()->prev()->style();
00534         if ( style )
00535         {
00536             KoParagStyle * newStyle = style->followingStyle();
00537             if ( newStyle && style != newStyle ) // different "following style" applied
00538             {
00539                 doUpdateCurrentFormat = true;
00540                 //currentFormat = textdoc->formatCollection()->format( cursor->parag()->paragFormat() );
00541                 //kdDebug(32500) << "KoTextFrameSet::doKeyboardAction currentFormat=" << currentFormat << " " << currentFormat->key() << endl;
00542             }
00543         }
00544         emit paragraphCreated( cursor->parag() );
00545 
00546     } break;
00547     case ActionKill:
00548         // Nothing to kill if at end of very last paragraph
00549         if ( !cursor->atParagEnd() || cursor->parag()->next() ) {
00550             checkUndoRedoInfo( cursor, UndoRedoInfo::Delete );
00551             if ( !undoRedoInfo.valid() ) {
00552                 newPlaceHolderCommand( i18n("Delete Text") );
00553                 undoRedoInfo.id = cursor->parag()->paragId();
00554                 undoRedoInfo.index = cursor->index();
00555                 undoRedoInfo.text = QString::null;
00556                 undoRedoInfo.oldParagLayouts << parag->paragLayout();
00557             }
00558             if ( cursor->atParagEnd() ) {
00559                 // Get paraglayout from next parag (next can't be 0 here)
00560                 KoParagLayout paragLayout = parag->next()->paragLayout();
00561                 if ( cursor->remove() )
00562                 {
00563                     m_lastFormatted = cursor->parag();
00564                     undoRedoInfo.text += "\n";
00565                     undoRedoInfo.oldParagLayouts << paragLayout;
00566                 }
00567             } else {
00568                 int oldLen = undoRedoInfo.text.length();
00569                 undoRedoInfo.text += cursor->parag()->string()->toString().mid( cursor->index() );
00570                 for ( int i = cursor->index(); i < cursor->parag()->length(); ++i )
00571                     copyCharFormatting( cursor->parag(), i, oldLen + i - cursor->index(), true );
00572                 cursor->killLine();
00573                 emit paragraphModified( cursor->parag(), RemoveChar, cursor->index(), cursor->parag()->length()-cursor->index() );
00574             }
00575         }
00576         break;
00577     }
00578 
00579     if ( !undoRedoInfo.customItemsMap.isEmpty() )
00580         clearUndoRedoInfo();
00581 
00582     formatMore( 2 );
00583     emit repaintChanged( this );
00584     emit ensureCursorVisible();
00585     emit showCursor();
00586     emit updateUI( doUpdateCurrentFormat );
00587 }
00588 
00589 void KoTextObject::insert( KoTextCursor * cursor, KoTextFormat * currentFormat,
00590                            const QString &txt, bool checkNewLine,
00591                            bool removeSelected, const QString & commandName,
00592                            CustomItemsMap customItemsMap, int selectionId, bool repaint )
00593 {
00594     if ( protectContent() )
00595         return;
00596     //kdDebug(32500) << "KoTextObject::insert txt=" << txt << endl;
00597     bool tinyRepaint = !checkNewLine;
00598     if ( repaint )
00599         emit hideCursor();
00600     if ( textdoc->hasSelection( selectionId, true ) && removeSelected  ) {
00601         if( customItemsMap.isEmpty())
00602         {
00603             emitNewCommand(replaceSelectionCommand( cursor, txt, selectionId, commandName, repaint ));
00604             return;
00605         }
00606         else
00607         {
00608             KCommand* removeSelCmd = removeSelectedTextCommand( cursor, selectionId );
00609             if (removeSelCmd)
00610                 emitNewCommand( removeSelCmd );
00611             tinyRepaint = false;
00612         }
00613     }
00614     KoTextCursor c2 = *cursor;
00615     if ( !customItemsMap.isEmpty() )
00616         clearUndoRedoInfo();
00617     checkUndoRedoInfo( cursor, UndoRedoInfo::Insert );
00618     if ( !undoRedoInfo.valid() ) {
00619         if ( !commandName.isNull() ) // see replace-selection
00620             newPlaceHolderCommand( commandName );
00621         undoRedoInfo.id = cursor->parag()->paragId();
00622         undoRedoInfo.index = cursor->index();
00623         undoRedoInfo.text = QString::null;
00624     }
00625     int oldLen = undoRedoInfo.text.length();
00626     KoTextCursor oldCursor = *cursor;
00627     bool wasChanged = cursor->parag()->hasChanged();
00628     int origLine; // the line the cursor was on before the insert
00629     oldCursor.parag()->lineStartOfChar( oldCursor.index(), 0, &origLine );
00630 
00631     cursor->insert( txt, checkNewLine );  // insert the text
00632     setLastFormattedParag( checkNewLine ? oldCursor.parag() : cursor->parag() );
00633 
00634     if ( !customItemsMap.isEmpty() ) {
00635         customItemsMap.insertItems( oldCursor, txt.length() );
00636         undoRedoInfo.customItemsMap = customItemsMap;
00637         tinyRepaint = false;
00638     }
00639 
00640     textdoc->setSelectionStart( KoTextDocument::Temp, &oldCursor );
00641     textdoc->setSelectionEnd( KoTextDocument::Temp, cursor );
00642     //kdDebug(32500) << "KoTextObject::insert setting format " << currentFormat << endl;
00643     textdoc->setFormat( KoTextDocument::Temp, currentFormat, KoTextFormat::Format );
00644     textdoc->setFormat( KoTextDocument::InputMethodPreedit, currentFormat, KoTextFormat::Format );
00645     textdoc->removeSelection( KoTextDocument::Temp );
00646 
00647     if ( !customItemsMap.isEmpty() ) {
00648         // Some custom items (e.g. variables) depend on the format
00649         CustomItemsMap::Iterator it = customItemsMap.begin();
00650         for ( ; it != customItemsMap.end(); ++it )
00651             it.data()->resize();
00652     }
00653 
00654     // Speed optimization: if we only type a char, and it doesn't
00655     // invalidate the next parag, only format the current one
00656     // ### This idea is wrong. E.g. when the last parag grows and must create a new page.
00657 #if 0
00658     KoTextParag *parag = cursor->parag();
00659     if ( !checkNewLine && m_lastFormatted == parag && ( !parag->next() || parag->next()->isValid() ) )
00660     {
00661         parag->format();
00662         m_lastFormatted = m_lastFormatted->next();
00663     }
00664 #endif
00665     // Call formatMore until necessary. This will create new pages if needed.
00666     // Doing this here means callers don't have to do it, and cursor can be positionned correctly.
00667     ensureFormatted( cursor->parag() );
00668 
00669     // Speed optimization: if we only type a char, only repaint from current line
00670     // (In fact the one before. When typing a space, a word could move up to the
00671     // line before, we need to repaint it too...)
00672     if ( !checkNewLine && tinyRepaint && !wasChanged )
00673     {
00674         // insert() called format() which called setChanged().
00675         // We're reverting that, and calling setLineChanged() only.
00676         Q_ASSERT( cursor->parag() == oldCursor.parag() ); // no newline!
00677         KoTextParag* parag = cursor->parag();
00678     // If the new char led to a new line,
00679     // the wordwrap could have changed on the line above
00680     // This is why we use origLine and not calling lineStartOfChar here.
00681         parag->setChanged( false );
00682         parag->setLineChanged( origLine - 1 ); // if origLine=0, it'll pass -1, which is 'all'
00683     }
00684 
00685     if ( repaint ) {
00686         emit repaintChanged( this );
00687         emit ensureCursorVisible();
00688         emit showCursor();
00689         // we typed the first char of a paragraph in AlignAuto mode -> show correct alignment in UI
00690         if ( oldCursor.index() == 0 && oldCursor.parag()->alignment() == Qt::AlignAuto )
00691             emit updateUI( true );
00692 
00693     }
00694     undoRedoInfo.text += txt;
00695     for ( int i = 0; i < (int)txt.length(); ++i ) {
00696         if ( txt[ oldLen + i ] != '\n' )
00697             copyCharFormatting( c2.parag(), c2.index(), oldLen + i, false );
00698         c2.gotoNextLetter();
00699     }
00700 
00701     if ( !removeSelected ) {
00702         // ## not sure why we do this. I'd prefer leaving the selection unchanged...
00703         // but then it'd need adjustements in the offsets etc.
00704         if ( textdoc->removeSelection( selectionId ) && repaint )
00705             selectionChangedNotify(); // does the repaint
00706     }
00707     if ( !customItemsMap.isEmpty() ) {
00708         clearUndoRedoInfo();
00709     }
00710 
00711     // Notifications
00712     emit paragraphModified( oldCursor.parag(), AddChar, cursor->index(), txt.length() );
00713     // TODO
00714     // if (checkNewLine)
00715     //     emit paragraphCreated for every paragraph from oldCursor->parag()->next() until cursor->parag()
00716 }
00717 
00718 void KoTextObject::pasteText( KoTextCursor * cursor, const QString & text, KoTextFormat * currentFormat, bool removeSelected )
00719 {
00720     if ( protectContent() )
00721         return;
00722     kdDebug(32500) << "KoTextObject::pasteText cursor parag=" << cursor->parag()->paragId() << endl;
00723     QString t = text;
00724     // Need to convert CRLF to NL
00725     QRegExp crlf( QString::fromLatin1("\r\n") );
00726     t.replace( crlf, QChar('\n') );
00727     // Convert non-printable chars
00728     for ( int i=0; (uint) i<t.length(); i++ ) {
00729         if ( t[ i ] < ' ' && t[ i ] != '\n' && t[ i ] != '\t' )
00730             t[ i ] = ' ';
00731     }
00732     if ( !t.isEmpty() )
00733     {
00734         insert( cursor, currentFormat, t, true /*checkNewLine*/, removeSelected, i18n("Paste Text") );
00735         formatMore( 2 );
00736         emit repaintChanged( this );
00737     }
00738 }
00739 
00740 KCommand* KoTextObject::setParagLayoutCommand( KoTextCursor * cursor, const KoParagLayout& paragLayout,
00741                                                int selectionId, int paragLayoutFlags,
00742                                                int marginIndex, bool createUndoRedo )
00743 {
00744     if ( protectContent() )
00745         return 0;
00746     storeParagUndoRedoInfo( cursor, selectionId );
00747     undoRedoInfo.type = UndoRedoInfo::Invalid; // tricky, we don't want clear() to create a command
00748     if ( paragLayoutFlags != 0 )
00749     {
00750         emit hideCursor();
00751         if ( !textdoc->hasSelection( selectionId, true ) ) {
00752             cursor->parag()->setParagLayout( paragLayout, paragLayoutFlags, marginIndex );
00753             setLastFormattedParag( cursor->parag() );
00754         } else {
00755             KoTextParag *start = textdoc->selectionStart( selectionId );
00756             KoTextParag *end = textdoc->selectionEnd( selectionId );
00757             for ( ; start && start != end->next() ; start = start->next() ) {
00758                 if ( paragLayoutFlags == KoParagLayout::BulletNumber && start->length() <= 1 )
00759                     continue; // don't apply to empty paragraphs (#25742, #34062)
00760                 start->setParagLayout( paragLayout, paragLayoutFlags, marginIndex );
00761             }
00762             setLastFormattedParag( start );
00763         }
00764 
00765         formatMore( 2 );
00766         emit repaintChanged( this );
00767         emit showCursor();
00768         emit updateUI( true );
00769 
00770         if ( createUndoRedo )
00771         {
00772             //kdDebug(32500) << "KoTextObject::applyStyle KoTextParagCommand" << endl;
00773             KoTextDocCommand * cmd = new KoTextParagCommand( textdoc, undoRedoInfo.id, undoRedoInfo.eid,
00774                                                              undoRedoInfo.oldParagLayouts,
00775                                                              paragLayout, paragLayoutFlags,
00776                                                              (QStyleSheetItem::Margin)marginIndex );
00777             textdoc->addCommand( cmd );
00778             return new KoTextCommand( this, /*cmd, */"related to KoTextParagCommand" );
00779         }
00780     }
00781     return 0;
00782 }
00783 
00784 
00785 void KoTextObject::applyStyle( KoTextCursor * cursor, const KoParagStyle * newStyle,
00786                                int selectionId,
00787                                int paragLayoutFlags, int formatFlags,
00788                                bool createUndoRedo, bool interactive )
00789 {
00790     KCommand *cmd = applyStyleCommand( cursor, newStyle, selectionId,
00791                                        paragLayoutFlags, formatFlags,
00792                                        createUndoRedo, interactive );
00793     if ( createUndoRedo && cmd )
00794         emit newCommand( cmd );
00795     else
00796         Q_ASSERT( !cmd ); // mem leak, if applyStyleCommand created a command despite createUndoRedo==false!
00797 }
00798 
00799 KCommand *KoTextObject::applyStyleCommand( KoTextCursor * cursor, const KoParagStyle * newStyle,
00800                                int selectionId,
00801                                int paragLayoutFlags, int formatFlags,
00802                                bool createUndoRedo, bool interactive )
00803 {
00804     if ( protectContent())
00805         return 0L;
00806     if ( interactive )
00807         emit hideCursor();
00808     if ( !textdoc->hasSelection( selectionId, true ) && !cursor)
00809         return 0L;
00815     KMacroCommand * macroCmd = createUndoRedo ? new KMacroCommand( i18n("Apply Style %1").
00816                                                                    arg(newStyle->displayName() ) ) : 0;
00817 
00818     // 1
00819     //kdDebug(32500) << "KoTextObject::applyStyle setParagLayout" << endl;
00820     KCommand* cmd = setParagLayoutCommand( cursor, newStyle->paragLayout(), selectionId, paragLayoutFlags, -1, createUndoRedo );
00821     if ( cmd )
00822         macroCmd->addCommand( cmd );
00823 
00824     // 2
00825     //kdDebug(32500) << "KoTextObject::applyStyle gathering text and formatting" << endl;
00826     KoTextParag * firstParag;
00827     KoTextParag * lastParag;
00828     if ( !textdoc->hasSelection( selectionId, true ) ) {
00829         // No selection -> apply style formatting to the whole paragraph
00830         firstParag = cursor->parag();
00831         lastParag = cursor->parag();
00832     }
00833     else
00834     {
00835         firstParag = textdoc->selectionStart( selectionId );
00836         lastParag = textdoc->selectionEnd( selectionId );
00837     }
00838 
00839     if ( formatFlags != 0 )
00840     {
00841         KoTextFormat * newFormat = textdoc->formatCollection()->format( &newStyle->format() );
00842 
00843         if ( createUndoRedo )
00844         {
00845             QValueList<KoTextFormat *> lstFormats;
00846             //QString str;
00847             for ( KoTextParag * parag = firstParag ; parag && parag != lastParag->next() ; parag = parag->next() )
00848             {
00849                 //str += parag->string()->toString() + '\n';
00850                 lstFormats.append( parag->paragFormat() );
00851             }
00852             KoTextCursor c1( textdoc );
00853             c1.setParag( firstParag );
00854             c1.setIndex( 0 );
00855             KoTextCursor c2( textdoc );
00856             c2.setParag( lastParag );
00857             c2.setIndex( lastParag->string()->length() );
00858             undoRedoInfo.clear();
00859             undoRedoInfo.type = UndoRedoInfo::Invalid; // same trick
00860             readFormats( c1, c2 ); // gather char-format info but not paraglayouts nor customitems
00861 
00862             KoTextDocCommand * cmd = new KoTextFormatCommand( textdoc, firstParag->paragId(), 0,
00863                                                          lastParag->paragId(), c2.index(),
00864                                                          undoRedoInfo.text.rawData(), newFormat,
00865                                                          formatFlags );
00866             textdoc->addCommand( cmd );
00867             macroCmd->addCommand( new KoTextCommand( this, /*cmd, */"related to KoTextFormatCommand" ) );
00868 
00869             // sub-command for '3' (paragFormat)
00870             cmd = new KoParagFormatCommand( textdoc, firstParag->paragId(), lastParag->paragId(),
00871                                             lstFormats, newFormat );
00872             textdoc->addCommand( cmd );
00873             macroCmd->addCommand( new KoTextCommand( this, /*cmd, */"related to KoParagFormatCommand" ) );
00874         }
00875 
00876         // apply '2' and '3' (format)
00877         for ( KoTextParag * parag = firstParag ; parag && parag != lastParag->next() ; parag = parag->next() )
00878         {
00879             //kdDebug(32500) << "KoTextObject::applyStyle parag:" << parag->paragId()
00880             //               << ", from 0 to " << parag->string()->length() << ", format=" << newFormat << endl;
00881             parag->setFormat( 0, parag->string()->length(), newFormat, true, formatFlags );
00882             parag->setFormat( newFormat );
00883         }
00884         //currentFormat = textdoc->formatCollection()->format( newFormat );
00885         //kdDebug(32500) << "KoTextObject::applyStyle currentFormat=" << currentFormat << " " << currentFormat->key() << endl;
00886     }
00887 
00888     //resize all variables after applying the style
00889     QPtrListIterator<KoTextCustomItem> cit( textdoc->allCustomItems() );
00890     for ( ; cit.current() ; ++cit )
00891         cit.current()->resize();
00892 
00893 
00894     if ( interactive )
00895     {
00896         setLastFormattedParag( firstParag );
00897         formatMore( 2 );
00898         emit repaintChanged( this );
00899         emit updateUI( true );
00900         emit showCursor();
00901     }
00902 
00903     undoRedoInfo.clear();
00904 
00905     return macroCmd;
00906 }
00907 
00908 void KoTextObject::applyStyleChange( KoStyleChangeDefMap changed )
00909 {
00910 #if 0 //#ifndef NDEBUG
00911     kdDebug(32500) << "KoTextObject::applyStyleChange " << changed.count() << " styles." << endl;
00912     for( KoStyleChangeDefMap::const_iterator it = changed.begin(); it != changed.end(); ++it ) {
00913         kdDebug(32500) << " " << it.key()->name()
00914                        << " paragLayoutChanged=" << (*it).paragLayoutChanged
00915                        << " formatChanged=" << (*it).formatChanged
00916                        << endl;
00917     }
00918 #endif
00919 
00920     KoTextParag *p = textdoc->firstParag();
00921     while ( p ) {
00922         KoStyleChangeDefMap::Iterator it = changed.find( p->style() );
00923         if ( it != changed.end() )
00924         {
00925             if ( (*it).paragLayoutChanged == -1 || (*it).formatChanged == -1 ) // Style has been deleted
00926             {
00927                 p->setStyle( m_defaultStyle ); // keeps current formatting
00928                 // TODO, make this undoable somehow
00929             }
00930             else
00931             {
00932                 // Apply this style again, to get the changes
00933                 KoTextCursor cursor( textdoc );
00934                 cursor.setParag( p );
00935                 cursor.setIndex( 0 );
00936                 //kdDebug(32500) << "KoTextObject::applyStyleChange applying to paragraph " << p << " " << p->paragId() << endl;
00937                 applyStyle( &cursor, it.key(),
00938                             -1, // A selection we can't possibly have
00939                             (*it).paragLayoutChanged, (*it).formatChanged,
00940                             false, false ); // don't create undo/redo, not interactive
00941             }
00942         } else {
00943             //kdDebug(32500) << "KoTextObject::applyStyleChange leaving paragraph unchanged: " << p << " " << p->paragId() << endl;
00944         }
00945 
00946         p = p->next();
00947     }
00948     setLastFormattedParag( textdoc->firstParag() );
00949     formatMore( 2 );
00950     emit repaintChanged( this );
00951     emit updateUI( true );
00952 }
00953 
00955 KCommand *KoTextObject::setFormatCommand( const KoTextFormat *format, int flags, bool zoomFont )
00956 {
00957     textdoc->selectAll( KoTextDocument::Temp );
00958     KCommand *cmd = setFormatCommand( 0L, 0L, format, flags, zoomFont, KoTextDocument::Temp );
00959     textdoc->removeSelection( KoTextDocument::Temp );
00960     return cmd;
00961 }
00962 
00963 KCommand * KoTextObject::setFormatCommand( KoTextCursor * cursor, KoTextFormat ** pCurrentFormat, const KoTextFormat *format, int flags, bool /*zoomFont*/, int selectionId )
00964 {
00965     KCommand *ret = 0;
00966     if ( protectContent() )
00967         return ret;
00968 
00969     KoTextFormat* newFormat = 0;
00970     // Get new format from collection if
00971     // - caller has notion of a "current format" and new format is different
00972     // - caller has no notion of "current format", e.g. whole textobjects
00973     bool isNewFormat = ( pCurrentFormat && *pCurrentFormat && (*pCurrentFormat)->key() != format->key() );
00974     if ( isNewFormat || !pCurrentFormat )
00975     {
00976 #if 0
00977         int origFontSize = 0;
00978         if ( zoomFont ) // The format has a user-specified font (e.g. setting a style, or a new font size)
00979         {
00980             origFontSize = format->pointSize();
00981             format->setPointSize( zoomedFontSize( origFontSize ) );
00982             //kdDebug(32500) << "KoTextObject::setFormatCommand format " << format->key() << " zoomed from " << origFontSize << " to " << format->font().pointSizeFloat() << endl;
00983         }
00984 #endif
00985         // Remove ref to current format, if caller wanted that
00986         if ( pCurrentFormat )
00987             (*pCurrentFormat)->removeRef();
00988         // Find format in collection
00989         newFormat = textdoc->formatCollection()->format( format );
00990         if ( newFormat->isMisspelled() ) {
00991             KoTextFormat fNoMisspelled( *newFormat );
00992             newFormat->removeRef();
00993             fNoMisspelled.setMisspelled( false );
00994             newFormat = textdoc->formatCollection()->format( &fNoMisspelled );
00995         }
00996         if ( pCurrentFormat )
00997             (*pCurrentFormat) = newFormat;
00998     }
00999 
01000     if ( textdoc->hasSelection( selectionId, true ) ) {
01001         emit hideCursor();
01002         KoTextCursor c1 = textdoc->selectionStartCursor( selectionId );
01003         KoTextCursor c2 = textdoc->selectionEndCursor( selectionId );
01004         undoRedoInfo.clear();
01005         int id = c1.parag()->paragId();
01006         int index = c1.index();
01007         int eid = c2.parag()->paragId();
01008         int eindex = c2.index();
01009         readFormats( c1, c2 ); // read previous formatting info
01010         //kdDebug(32500) << "KoTextObject::setFormatCommand undoredo info done" << endl;
01011         textdoc->setFormat( selectionId, format, flags );
01012         if ( !undoRedoInfo.customItemsMap.isEmpty() )
01013         {
01014             // Some custom items (e.g. variables) depend on the format
01015             CustomItemsMap::Iterator it = undoRedoInfo.customItemsMap.begin();
01016             for ( ; it != undoRedoInfo.customItemsMap.end(); ++it )
01017                 it.data()->resize();
01018         }
01019         KoTextFormatCommand *cmd = new KoTextFormatCommand(
01020             textdoc, id, index, eid, eindex, undoRedoInfo.text.rawData(),
01021             format, flags );
01022         textdoc->addCommand( cmd );
01023         ret = new KoTextCommand( this, /*cmd, */i18n("Format Text") );
01024         undoRedoInfo.clear();
01025         setLastFormattedParag( c1.parag() );
01026         formatMore( 2 );
01027         emit repaintChanged( this );
01028         emit showCursor();
01029     }
01030     if ( isNewFormat ) {
01031         emit showCurrentFormat();
01032         //kdDebug(32500) << "KoTextObject::setFormatCommand index=" << cursor->index() << " length-1=" << cursor->parag()->length() - 1 << endl;
01033         if ( cursor && cursor->index() == cursor->parag()->length() - 1 ) {
01034             newFormat->addRef();
01035             cursor->parag()->string()->setFormat( cursor->index(), newFormat, TRUE );
01036             if ( cursor->parag()->length() == 1 ) {
01037                 newFormat->addRef();
01038                 cursor->parag()->setFormat( newFormat );
01039                 cursor->parag()->invalidate(0);
01040                 cursor->parag()->format();
01041                 emit repaintChanged( this );
01042             }
01043         }
01044     }
01045     return ret;
01046 }
01047 
01048 void KoTextObject::setFormat( KoTextCursor * cursor, KoTextFormat ** currentFormat, KoTextFormat *format, int flags, bool zoomFont )
01049 {
01050     if ( protectContent() )
01051         return;
01052     KCommand *cmd = setFormatCommand( cursor, currentFormat, format, flags, zoomFont );
01053     if (cmd)
01054         emit newCommand( cmd );
01055 }
01056 
01057 void KoTextObject::emitNewCommand(KCommand *cmd)
01058 {
01059     if(cmd)
01060         emit newCommand( cmd );
01061 }
01062 
01063 KCommand *KoTextObject::setCounterCommand( KoTextCursor * cursor, const KoParagCounter & counter, int selectionId  )
01064 {
01065     if ( protectContent() )
01066         return 0L;
01067     const KoParagCounter * curCounter = 0L;
01068     if(cursor)
01069         curCounter=cursor->parag()->counter();
01070     if ( !textdoc->hasSelection( selectionId, true ) &&
01071          curCounter && counter == *curCounter ) {
01072         return 0L;
01073     }
01074     emit hideCursor();
01075     storeParagUndoRedoInfo( cursor, selectionId );
01076     if ( !textdoc->hasSelection( selectionId, true ) && cursor) {
01077         cursor->parag()->setCounter( counter );
01078         setLastFormattedParag( cursor->parag() );
01079     } else {
01080         KoTextParag *start = textdoc->selectionStart( selectionId );
01081         KoTextParag *end = textdoc->selectionEnd( selectionId );
01082 #if 0
01083         // Special hack for BR25742, don't apply bullet to last empty parag of the selection
01084         if ( start != end && end->length() <= 1 )
01085         {
01086             end = end->prev();
01087             undoRedoInfo.eid = end->paragId();
01088         }
01089 #endif
01090         setLastFormattedParag( start );
01091         for ( ; start && start != end->next() ; start = start->next() )
01092         {
01093             if ( start->length() > 1 )  // don't apply to empty paragraphs (#25742, #34062)
01094                 start->setCounter( counter );
01095         }
01096     }
01097     formatMore( 2 );
01098     emit repaintChanged( this );
01099     if ( !undoRedoInfo.newParagLayout.counter )
01100         undoRedoInfo.newParagLayout.counter = new KoParagCounter;
01101     *undoRedoInfo.newParagLayout.counter = counter;
01102     KoTextParagCommand *cmd = new KoTextParagCommand(
01103         textdoc, undoRedoInfo.id, undoRedoInfo.eid,
01104         undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout,
01105         KoParagLayout::BulletNumber );
01106     textdoc->addCommand( cmd );
01107 
01108     undoRedoInfo.clear(); // type is still Invalid -> no command created
01109     emit showCursor();
01110     emit updateUI( true );
01111     return new KoTextCommand( this, /*cmd, */i18n("Change List Type") );
01112 }
01113 
01114 KCommand * KoTextObject::setAlignCommand( KoTextCursor * cursor, int align, int selectionId )
01115 {
01116     if ( protectContent() )
01117         return 0L;
01118     if ( !textdoc->hasSelection( selectionId, true ) && cursor &&
01119          (int)cursor->parag()->alignment() == align )
01120         return 0L; // No change needed.
01121 
01122     emit hideCursor();
01123     storeParagUndoRedoInfo( cursor ,selectionId );
01124     if ( !textdoc->hasSelection( selectionId, true ) &&cursor ) {
01125         cursor->parag()->setAlign(align);
01126         setLastFormattedParag( cursor->parag() );
01127     }
01128     else
01129     {
01130         KoTextParag *start = textdoc->selectionStart( selectionId );
01131         KoTextParag *end = textdoc->selectionEnd( selectionId  );
01132         setLastFormattedParag( start );
01133         for ( ; start && start != end->next() ; start = start->next() )
01134             start->setAlign(align);
01135     }
01136     formatMore( 2 );
01137     emit repaintChanged( this );
01138     undoRedoInfo.newParagLayout.alignment = align;
01139     KoTextParagCommand *cmd = new KoTextParagCommand(
01140         textdoc, undoRedoInfo.id, undoRedoInfo.eid,
01141         undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout,
01142         KoParagLayout::Alignment );
01143     textdoc->addCommand( cmd );
01144     undoRedoInfo.clear(); // type is still Invalid -> no command created
01145     emit showCursor();
01146     emit updateUI( true );
01147     return new KoTextCommand( this, /*cmd, */i18n("Change Alignment") );
01148 }
01149 
01150 KCommand * KoTextObject::setMarginCommand( KoTextCursor * cursor, QStyleSheetItem::Margin m, double margin , int selectionId ) {
01151     if ( protectContent() )
01152         return 0L;
01153 
01154     //kdDebug(32500) << "KoTextObject::setMargin " << m << " to value " << margin << endl;
01155     //kdDebug(32500) << "Current margin is " << cursor->parag()->margin(m) << endl;
01156     if ( !textdoc->hasSelection( selectionId, true ) && cursor &&
01157          cursor->parag()->margin(m) == margin )
01158         return 0L; // No change needed.
01159 
01160     emit hideCursor();
01161     storeParagUndoRedoInfo( cursor, selectionId );
01162     if ( !textdoc->hasSelection( selectionId, true )&&cursor ) {
01163         cursor->parag()->setMargin(m, margin);
01164         setLastFormattedParag( cursor->parag() );
01165     }
01166     else
01167     {
01168         KoTextParag *start = textdoc->selectionStart( selectionId );
01169         KoTextParag *end = textdoc->selectionEnd( selectionId );
01170         setLastFormattedParag( start );
01171         for ( ; start && start != end->next() ; start = start->next() )
01172             start->setMargin(m, margin);
01173     }
01174     formatMore( 2 );
01175     emit repaintChanged( this );
01176     undoRedoInfo.newParagLayout.margins[m] = margin;
01177     KoTextParagCommand *cmd = new KoTextParagCommand(
01178         textdoc, undoRedoInfo.id, undoRedoInfo.eid,
01179         undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout,
01180         KoParagLayout::Margins, m );
01181     textdoc->addCommand( cmd );
01182     QString name;
01183     if ( m == QStyleSheetItem::MarginFirstLine )
01184         name = i18n("Change First Line Indent");
01185     else if ( m == QStyleSheetItem::MarginLeft || m == QStyleSheetItem::MarginRight )
01186         name = i18n("Change Indent");
01187     else
01188         name = i18n("Change Paragraph Spacing");
01189     undoRedoInfo.clear();
01190     emit showCursor();
01191     emit updateUI( true );
01192     return  new KoTextCommand( this, /*cmd, */name );
01193 }
01194 
01195 KCommand * KoTextObject::setLineSpacingCommand( KoTextCursor * cursor, double spacing, KoParagLayout::SpacingType _type, int selectionId )
01196 {
01197     if ( protectContent() )
01198         return 0L;
01199     //kdDebug(32500) << "KoTextObject::setLineSpacing to value " << spacing << endl;
01200     //kdDebug(32500) << "Current spacing is " << cursor->parag()->kwLineSpacing() << endl;
01201     //kdDebug(32500) << "Comparison says " << ( cursor->parag()->kwLineSpacing() == spacing ) << endl;
01202     //kdDebug(32500) << "hasSelection " << textdoc->hasSelection( selectionId ) << endl;
01203     if ( !textdoc->hasSelection( selectionId, true ) && cursor &&
01204          cursor->parag()->kwLineSpacing() == spacing
01205         && cursor->parag()->kwLineSpacingType() == _type)
01206         return 0L; // No change needed.
01207 
01208     emit hideCursor();
01209     storeParagUndoRedoInfo( cursor, selectionId );
01210     if ( !textdoc->hasSelection( selectionId, true ) && cursor ) {
01211         cursor->parag()->setLineSpacing(spacing);
01212         cursor->parag()->setLineSpacingType( _type);
01213         setLastFormattedParag( cursor->parag() );
01214     }
01215     else
01216     {
01217         KoTextParag *start = textdoc->selectionStart( selectionId );
01218         KoTextParag *end = textdoc->selectionEnd( selectionId );
01219         setLastFormattedParag( start );
01220         for ( ; start && start != end->next() ; start = start->next() )
01221         {
01222             start->setLineSpacing(spacing);
01223             start->setLineSpacingType( _type);
01224         }
01225     }
01226     formatMore( 2 );
01227     emit repaintChanged( this );
01228     undoRedoInfo.newParagLayout.setLineSpacingValue( spacing );
01229     undoRedoInfo.newParagLayout.lineSpacingType = _type;
01230     KoTextParagCommand *cmd = new KoTextParagCommand(
01231         textdoc, undoRedoInfo.id, undoRedoInfo.eid,
01232         undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout,
01233         KoParagLayout::LineSpacing );
01234     textdoc->addCommand( cmd );
01235 
01236     undoRedoInfo.clear();
01237     emit showCursor();
01238     return new KoTextCommand( this, /*cmd, */i18n("Change Line Spacing") );
01239 }
01240 
01241 
01242 KCommand * KoTextObject::setBordersCommand( KoTextCursor * cursor, const KoBorder& leftBorder, const KoBorder& rightBorder, const KoBorder& topBorder, const KoBorder& bottomBorder , int selectionId )
01243 {
01244     if ( protectContent() )
01245         return 0L;
01246   if ( !textdoc->hasSelection( selectionId, true ) && cursor &&
01247        cursor->parag()->leftBorder() ==leftBorder &&
01248        cursor->parag()->rightBorder() ==rightBorder &&
01249        cursor->parag()->topBorder() ==topBorder &&
01250        cursor->parag()->bottomBorder() ==bottomBorder )
01251         return 0L; // No change needed.
01252 
01253     emit hideCursor();
01254     bool borderOutline = false;
01255     storeParagUndoRedoInfo( cursor, selectionId );
01256     if ( !textdoc->hasSelection( selectionId, true ) ) {
01257       cursor->parag()->setLeftBorder(leftBorder);
01258       cursor->parag()->setRightBorder(rightBorder);
01259       cursor->parag()->setBottomBorder(bottomBorder);
01260       cursor->parag()->setTopBorder(topBorder);
01261       setLastFormattedParag( cursor->parag() );
01262     }
01263     else
01264     {
01265         KoTextParag *start = textdoc->selectionStart( selectionId );
01266         KoTextParag *end = textdoc->selectionEnd( selectionId );
01267         setLastFormattedParag( start );
01268         KoBorder tmpBorder;
01269         tmpBorder.setPenWidth(0);
01270         for ( ; start && start != end->next() ; start = start->next() )
01271           {
01272             start->setLeftBorder(leftBorder);
01273             start->setRightBorder(rightBorder);
01274             //remove border
01275             start->setTopBorder(tmpBorder);
01276             start->setBottomBorder(tmpBorder);
01277           }
01278         end->setBottomBorder(bottomBorder);
01279         textdoc->selectionStart( selectionId )->setTopBorder(topBorder);
01280         borderOutline = true;
01281     }
01282     formatMore( 2 );
01283     emit repaintChanged( this );
01284     undoRedoInfo.newParagLayout.leftBorder=leftBorder;
01285     undoRedoInfo.newParagLayout.rightBorder=rightBorder;
01286     undoRedoInfo.newParagLayout.topBorder=topBorder;
01287     undoRedoInfo.newParagLayout.bottomBorder=bottomBorder;
01288 
01289     KoTextParagCommand *cmd = new KoTextParagCommand(
01290         textdoc, undoRedoInfo.id, undoRedoInfo.eid,
01291         undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout,
01292         KoParagLayout::Borders, (QStyleSheetItem::Margin)-1, borderOutline);
01293     textdoc->addCommand( cmd );
01294 
01295     undoRedoInfo.clear();
01296     emit showCursor();
01297     emit updateUI( true );
01298     return new KoTextCommand( this, /*cmd, */i18n("Change Borders") );
01299 }
01300 
01301 
01302 KCommand * KoTextObject::setTabListCommand( KoTextCursor * cursor, const KoTabulatorList &tabList, int selectionId  )
01303 {
01304     if ( protectContent() )
01305         return 0L;
01306     if ( !textdoc->hasSelection( selectionId, true ) && cursor &&
01307          cursor->parag()->tabList() == tabList )
01308         return 0L; // No change needed.
01309 
01310     emit hideCursor();
01311     storeParagUndoRedoInfo( cursor, selectionId );
01312 
01313     if ( !textdoc->hasSelection( selectionId, true ) && cursor ) {
01314         cursor->parag()->setTabList( tabList );
01315         setLastFormattedParag( cursor->parag() );
01316     }
01317     else
01318     {
01319         KoTextParag *start = textdoc->selectionStart( selectionId );
01320         KoTextParag *end = textdoc->selectionEnd( selectionId );
01321         setLastFormattedParag( start );
01322         for ( ; start && start != end->next() ; start = start->next() )
01323             start->setTabList( tabList );
01324     }
01325 
01326     formatMore( 2 );
01327     emit repaintChanged( this );
01328     undoRedoInfo.newParagLayout.setTabList( tabList );
01329     KoTextParagCommand *cmd = new KoTextParagCommand(
01330         textdoc, undoRedoInfo.id, undoRedoInfo.eid,
01331         undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout,
01332         KoParagLayout::Tabulator);
01333     textdoc->addCommand( cmd );
01334     undoRedoInfo.clear();
01335     emit showCursor();
01336     emit updateUI( true );
01337     return new KoTextCommand( this, /*cmd, */i18n("Change Tabulator") );
01338 }
01339 
01340 KCommand * KoTextObject::setParagDirectionCommand( KoTextCursor * cursor, QChar::Direction d, int selectionId )
01341 {
01342     if ( protectContent() )
01343         return 0L;
01344     if ( !textdoc->hasSelection( selectionId, true ) && cursor &&
01345          cursor->parag()->direction() == d )
01346         return 0L; // No change needed.
01347 
01348     emit hideCursor();
01349     storeParagUndoRedoInfo( cursor, selectionId );
01350 
01351     if ( !textdoc->hasSelection( selectionId, true ) && cursor ) {
01352         cursor->parag()->setDirection( d );
01353         setLastFormattedParag( cursor->parag() );
01354     }
01355     else
01356     {
01357         KoTextParag *start = textdoc->selectionStart( selectionId );
01358         KoTextParag *end = textdoc->selectionEnd( selectionId );
01359         setLastFormattedParag( start );
01360         for ( ; start && start != end->next() ; start = start->next() )
01361             start->setDirection( d );
01362     }
01363 
01364     formatMore( 2 );
01365     emit repaintChanged( this );
01367 #if 0
01368     undoRedoInfo.newParagLayout.direction = d;
01369     KoTextParagCommand *cmd = new KoTextParagCommand(
01370         textdoc, undoRedoInfo.id, undoRedoInfo.eid,
01371         undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout,
01372         KoParagLayout::Shadow);
01373     textdoc->addCommand( cmd );
01374 #endif
01375     undoRedoInfo.clear();
01376     emit showCursor();
01377     emit updateUI( true );
01378 #if 0
01379     return new KoTextCommand( this, /*cmd, */i18n("Change Shadow") );
01380 #else
01381     return 0L;
01382 #endif
01383 }
01384 
01385 void KoTextObject::removeSelectedText( KoTextCursor * cursor, int selectionId, const QString & cmdName, bool createUndoRedo )
01386 {
01387     if ( protectContent() )
01388         return ;
01389     emit hideCursor();
01390     if( createUndoRedo)
01391     {
01392         checkUndoRedoInfo( cursor, UndoRedoInfo::RemoveSelected );
01393         if ( !undoRedoInfo.valid() ) {
01394             textdoc->selectionStart( selectionId, undoRedoInfo.id, undoRedoInfo.index );
01395             undoRedoInfo.text = QString::null;
01396             newPlaceHolderCommand( cmdName.isNull() ? i18n("Remove Selected Text") : cmdName );
01397         }
01398     }
01399     KoTextCursor c1 = textdoc->selectionStartCursor( selectionId );
01400     KoTextCursor c2 = textdoc->selectionEndCursor( selectionId );
01401     readFormats( c1, c2, true, true );
01402     //kdDebug(32500) << "KoTextObject::removeSelectedText text=" << undoRedoInfo.text.toString() << endl;
01403 
01404     textdoc->removeSelectedText( selectionId, cursor );
01405 
01406     setLastFormattedParag( cursor->parag() );
01407     formatMore( 2 );
01408     emit repaintChanged( this );
01409     emit ensureCursorVisible();
01410     emit updateUI( true );
01411     emit showCursor();
01412     if(selectionId==KoTextDocument::Standard || selectionId==KoTextDocument::InputMethodPreedit)
01413         selectionChangedNotify();
01414     if ( createUndoRedo)
01415         undoRedoInfo.clear();
01416 }
01417 
01418 KCommand * KoTextObject::removeSelectedTextCommand( KoTextCursor * cursor, int selectionId, bool repaint )
01419 {
01420     if ( protectContent() )
01421         return 0L;
01422     if ( !textdoc->hasSelection( selectionId, true ) )
01423         return 0L;
01424 
01425     undoRedoInfo.clear();
01426     textdoc->selectionStart( selectionId, undoRedoInfo.id, undoRedoInfo.index );
01427     Q_ASSERT( undoRedoInfo.id >= 0 );
01428 
01429     KoTextCursor c1 = textdoc->selectionStartCursor( selectionId );
01430     KoTextCursor c2 = textdoc->selectionEndCursor( selectionId );
01431     readFormats( c1, c2, true, true );
01432 
01433     textdoc->removeSelectedText( selectionId, cursor );
01434 
01435     KMacroCommand *macroCmd = new KMacroCommand( i18n("Remove Selected Text") );
01436 
01437     KoTextDocCommand *cmd = deleteTextCommand( textdoc, undoRedoInfo.id, undoRedoInfo.index,
01438                                                  undoRedoInfo.text.rawData(),
01439                                                  undoRedoInfo.customItemsMap,
01440                                                  undoRedoInfo.oldParagLayouts );
01441     textdoc->addCommand(cmd);
01442     macroCmd->addCommand(new KoTextCommand( this, /*cmd, */QString::null ));
01443 
01444     if(!undoRedoInfo.customItemsMap.isEmpty())
01445         undoRedoInfo.customItemsMap.deleteAll( macroCmd );
01446 
01447     undoRedoInfo.type = UndoRedoInfo::Invalid; // we don't want clear() to create a command
01448     undoRedoInfo.clear();
01449     if ( repaint )
01450         selectionChangedNotify();
01451     return macroCmd;
01452 }
01453 
01454 KCommand* KoTextObject::replaceSelectionCommand( KoTextCursor * cursor, const QString & replacement,
01455                                                  int selectionId, const QString & cmdName, bool repaint,
01456                                                  bool checkNewLine )
01457 {
01458     if ( protectContent() )
01459         return 0L;
01460     if ( repaint )
01461         emit hideCursor();
01462     KMacroCommand * macroCmd = new KMacroCommand( cmdName );
01463 
01464     // Remember formatting
01465     KoTextCursor c1 = textdoc->selectionStartCursor( selectionId );
01466     KoTextFormat * format = c1.parag()->at( c1.index() )->format();
01467     format->addRef();
01468 
01469     // Remove selected text, if any
01470     KCommand* removeSelCmd = removeSelectedTextCommand( cursor, selectionId, repaint );
01471     if ( removeSelCmd )
01472         macroCmd->addCommand( removeSelCmd );
01473 
01474     // Insert replacement
01475     insert( cursor, format,
01476             replacement, checkNewLine, false, QString::null /* no place holder command */,
01477             CustomItemsMap(), selectionId, repaint );
01478 
01479     KoTextDocCommand * cmd = new KoTextInsertCommand( textdoc, undoRedoInfo.id, undoRedoInfo.index,
01480                                                   undoRedoInfo.text.rawData(),
01481                                                   CustomItemsMap(), undoRedoInfo.oldParagLayouts );
01482     textdoc->addCommand( cmd );
01483     macroCmd->addCommand( new KoTextCommand( this, /*cmd, */QString::null ) );
01484 
01485     undoRedoInfo.type = UndoRedoInfo::Invalid; // we don't want clear() to create a command
01486     undoRedoInfo.clear();
01487 
01488     format->removeRef();
01489 
01490     setLastFormattedParag( c1.parag() );
01491     if ( repaint ) // false during search/replace
01492     {
01493         formatMore( 2 );
01494         emit repaintChanged( this );
01495         emit ensureCursorVisible();
01496         emit updateUI( true );
01497         emit showCursor();
01498         if(selectionId==KoTextDocument::Standard)
01499             selectionChangedNotify();
01500     }
01501     return macroCmd;
01502 }
01503 
01504 KCommand * KoTextObject::insertParagraphCommand( KoTextCursor *cursor )
01505 {
01506     if ( protectContent() )
01507         return 0L;
01508     return replaceSelectionCommand( cursor, "\n", KoTextDocument::Standard, QString::null );
01509 }
01510 
01511 void KoTextObject::highlightPortion( KoTextParag * parag, int index, int length, bool repaint )
01512 {
01513     if ( !m_highlightSelectionAdded )
01514     {
01515         textdoc->addSelection( HighlightSelection );
01516         textdoc->setSelectionColor( HighlightSelection,
01517                                     QApplication::palette().color( QPalette::Active, QColorGroup::Dark ) );
01518         textdoc->setInvertSelectionText( HighlightSelection, true );
01519          m_highlightSelectionAdded = true;
01520     }
01521 
01522     removeHighlight(repaint); // remove previous highlighted selection
01523     KoTextCursor cursor( textdoc );
01524     cursor.setParag( parag );
01525     cursor.setIndex( index );
01526     textdoc->setSelectionStart( HighlightSelection, &cursor );
01527     cursor.setIndex( index + length );
01528     textdoc->setSelectionEnd( HighlightSelection, &cursor );
01529     if ( repaint ) {
01530         parag->setChanged( true );
01531         emit repaintChanged( this );
01532     }
01533 }
01534 
01535 void KoTextObject::removeHighlight(bool repaint)
01536 {
01537     if ( textdoc->hasSelection( HighlightSelection, true ) )
01538     {
01539         KoTextParag * oldParag = textdoc->selectionStart( HighlightSelection );
01540         oldParag->setChanged( true );
01541         textdoc->removeSelection( HighlightSelection );
01542     }
01543     if ( repaint )
01544         emit repaintChanged( this );
01545 }
01546 
01547 void KoTextObject::selectAll( bool select )
01548 {
01549     if ( !select )
01550         textdoc->removeSelection( KoTextDocument::Standard );
01551     else
01552         textdoc->selectAll( KoTextDocument::Standard );
01553     selectionChangedNotify();
01554 }
01555 
01556 void KoTextObject::selectionChangedNotify( bool enableActions /* = true */)
01557 {
01558     emit repaintChanged( this );
01559     if ( enableActions )
01560         emit selectionChanged( hasSelection() );
01561 }
01562 
01563 void KoTextObject::setViewArea( QWidget* w, int maxY )
01564 {
01565     m_mapViewAreas.replace( w, maxY );
01566 }
01567 
01568 void KoTextObject::setLastFormattedParag( KoTextParag *parag )
01569 {
01570     //kdDebug() << k_funcinfo << parag << " (" << ( parag ? QString::number(parag->paragId()) : QString("(null)") ) << ")" << endl;
01571     if ( !m_lastFormatted || !parag || m_lastFormatted->paragId() >= parag->paragId() ) {
01572         m_lastFormatted = parag;
01573     }
01574 }
01575 
01576 void KoTextObject::ensureFormatted( KoTextParag * parag, bool emitAfterFormatting /* = true */ )
01577 {
01578     if ( !textdoc->lastParag() )
01579         return; // safety test
01580     //kdDebug(32500) << name() << " ensureFormatted " << parag->paragId() << endl;
01581     if ( !parag->isValid() && m_lastFormatted == 0 )
01582         m_lastFormatted = parag; // bootstrap
01583 
01584     while ( !parag->isValid() )
01585     {
01586         if ( !m_lastFormatted ) {
01587             kdWarning() << "ensureFormatted for parag " << parag << " " << parag->paragId() << " still not formatted, but m_lastFormatted==0" << endl;
01588             return;
01589         }
01590         // The paragid diff is "a good guess". The >=1 is a safety measure ;)
01591         bool ret = formatMore( QMAX( 1, parag->paragId() - m_lastFormatted->paragId() ), emitAfterFormatting );
01592         if ( !ret ) { // aborted
01593             //kdDebug(32500) << "ensureFormatted aborted!" << endl;
01594             break;
01595         }
01596     }
01597     //kdDebug(32500) << name() << " ensureFormatted " << parag->paragId() << " done" << endl;
01598 }
01599 
01600 bool KoTextObject::formatMore( int count /* = 10 */, bool emitAfterFormatting /* = true */ )
01601 {
01602     if ( ( !m_lastFormatted && d->afterFormattingEmitted )
01603          || !m_visible || m_availableHeight == -1 )
01604         return false;
01605 
01606     if ( !textdoc->lastParag() )
01607         return false; // safety test
01608 
01609     if ( d->abortFormatting ) {
01610         d->abortFormatting = false;
01611         return false;
01612     }
01613 
01614     if ( count == 0 )
01615     {
01616         formatTimer->start( interval, TRUE );
01617         return true;
01618     }
01619 
01620     int bottom = 0;
01621     if ( m_lastFormatted )
01622     {
01623         d->afterFormattingEmitted = false;
01624 
01625         int viewsBottom = 0;
01626         QMapIterator<QWidget *, int> mapIt = m_mapViewAreas.begin();
01627         for ( ; mapIt != m_mapViewAreas.end() ; ++mapIt )
01628             viewsBottom = QMAX( viewsBottom, mapIt.data() );
01629 
01630 #ifdef DEBUG_FORMAT_MORE
01631         kdDebug(32500) << "formatMore " << name()
01632                        << " lastFormatted id=" << m_lastFormatted->paragId()
01633                        << " lastFormatted's top=" << m_lastFormatted->rect().top()
01634                        << " lastFormatted's height=" << m_lastFormatted->rect().height()
01635                        << " count=" << count << " viewsBottom=" << viewsBottom
01636                        << " availableHeight=" << m_availableHeight << endl;
01637 #endif
01638         if ( m_lastFormatted->prev() == 0 )
01639         {
01640             emit formattingFirstParag();
01641 #ifdef TIMING_FORMAT
01642             kdDebug(32500) << "formatMore " << name() << ". First parag -> starting timer" << endl;
01643             m_time.start();
01644 #endif
01645         }
01646 
01647         // Stop if we have formatted everything or if we need more space
01648         // Otherwise, stop formatting after "to" paragraphs,
01649         // but make sure we format everything the views need
01650         int i;
01651         for ( i = 0;
01652               m_lastFormatted && bottom + m_lastFormatted->rect().height() <= m_availableHeight &&
01653                   ( i < count || bottom <= viewsBottom ) ; ++i )
01654         {
01655             KoTextParag* parag = m_lastFormatted;
01656 #ifdef DEBUG_FORMAT_MORE
01657             kdDebug(32500) << "formatMore formatting " << parag << " id=" << parag->paragId() << endl;
01658 #endif
01659             assert( parag->string() ); // i.e. not deleted
01660             parag->format();
01661             bottom = parag->rect().top() + parag->rect().height();
01662 #if 0 //def DEBUG_FORMAT_MORE
01663             kdDebug(32500) << "formatMore(inside) top=" << parag->rect().top()
01664                       << " height=" << parag->rect().height()
01665                       << " bottom=" << bottom << " m_lastFormatted(next parag) = " << m_lastFormatted->next() << endl;
01666 #endif
01667 
01668             // Check for Head 1 (i.e. section) titles, and emit them - for the Section variable
01669             if ( parag->counter() && parag->counter()->numbering() == KoParagCounter::NUM_CHAPTER
01670                  && parag->counter()->depth() == 0 )
01671                 emit chapterParagraphFormatted( parag );
01672 
01673             if ( d->abortFormatting ) {
01674 #ifdef DEBUG_FORMAT_MORE
01675                 kdDebug(32500) << "formatMore formatting aborted. " << endl;
01676 #endif
01677                 d->abortFormatting = false;
01678                 return false;
01679             }
01680 
01681             if ( parag != m_lastFormatted )
01682                 kdWarning() << "Some code changed m_lastFormatted during formatting! Was " << parag->paragId() << ", is now " << m_lastFormatted->paragId() << endl;
01683 #if 0 // This happens now that formatting can 'abort' (e.g. due to page not yet created)
01684             else if (!parag->isValid())
01685                 kdWarning() << "PARAGRAPH " << parag->paragId() << " STILL INVALID AFTER FORMATTING" << endl;
01686 #endif
01687             m_lastFormatted = parag->next();
01688         }
01689     }
01690     else // formatting was done previously, but not emit afterFormatting
01691     {
01692         QRect rect = textdoc->lastParag()->rect();
01693         bottom = rect.top() + rect.height();
01694     }
01695 #ifdef DEBUG_FORMAT_MORE
01696     QString id;
01697     if ( m_lastFormatted ) id = QString(" (%1)").arg(m_lastFormatted->paragId());
01698     kdDebug(32500) << "formatMore finished formatting. "
01699                    << " bottom=" << bottom
01700                    << " m_lastFormatted=" << m_lastFormatted << id
01701                    << endl;
01702 #endif
01703 
01704     if ( emitAfterFormatting )
01705     {
01706         d->afterFormattingEmitted = true;
01707         bool abort = false;
01708         // Check if we need more space - for proper default value of 'abort'
01709         if ( ( bottom > m_availableHeight ) ||   // this parag is already off page
01710              ( m_lastFormatted && bottom + m_lastFormatted->rect().height() > m_availableHeight ) ) // or next parag will be off page
01711             abort = true;
01712         emit afterFormatting( bottom, m_lastFormatted, &abort );
01713         if ( abort )
01714             return false;
01715         else if ( m_lastFormatted ) // we got more space, keep formatting then
01716             return formatMore( 2 );
01717     }
01718 
01719     // Now let's see when we'll need to get back here.
01720     if ( m_lastFormatted )
01721     {
01722         formatTimer->start( interval, TRUE );
01723 #ifdef DEBUG_FORMAT_MORE
01724         kdDebug(32500) << name() << " formatMore: will have to format more. formatTimer->start with interval=" << interval << endl;
01725 #endif
01726     }
01727     else
01728     {
01729         interval = QMAX( 0, interval );
01730 #ifdef DEBUG_FORMAT_MORE
01731         kdDebug(32500) << name() << " formatMore: all formatted interval=" << interval << endl;
01732 #endif
01733 #ifdef TIMING_FORMAT
01734         //if ( frameSetInfo() == FI_BODY )
01735         kdDebug(32500) << "formatMore: " << name() << " all formatted. Took "
01736                        << (double)(m_time.elapsed()) / 1000 << " seconds." << endl;
01737 #endif
01738     }
01739     return true;
01740 }
01741 
01742 void KoTextObject::abortFormatting()
01743 {
01744     d->abortFormatting = true;
01745 }
01746 
01747 void KoTextObject::doChangeInterval()
01748 {
01749     //kdDebug(32500) << "KoTextObject::doChangeInterval back to interval=0" << endl;
01750     interval = 0;
01751 }
01752 
01753 void KoTextObject::typingStarted()
01754 {
01755     //kdDebug(32500) << "KoTextObject::typingStarted" << endl;
01756     changeIntervalTimer->stop();
01757     interval = 10;
01758 }
01759 
01760 void KoTextObject::typingDone()
01761 {
01762     changeIntervalTimer->start( 100, TRUE );
01763 }
01764 
01765 
01766 // helper for changeCaseOfText
01767 KCommand *KoTextObject::changeCaseOfTextParag( int cursorPosStart, int cursorPosEnd,
01768                                                KoChangeCaseDia::TypeOfCase _type,
01769                                                KoTextCursor *cursor, KoTextParag *parag )
01770 {
01771     if ( protectContent() )
01772         return 0L;
01773 
01774     KMacroCommand * macroCmd = new KMacroCommand( i18n("Change Case") );
01775     KoTextFormat *curFormat = parag->paragraphFormat();
01776     const QString text = parag->string()->toString().mid( cursorPosStart, cursorPosEnd - cursorPosStart );
01777     int posStart = cursorPosStart;
01778     int posEnd = cursorPosStart;
01779     KoTextCursor c1( textdoc );
01780     KoTextCursor c2( textdoc );
01781     //kdDebug() << k_funcinfo << "from " << cursorPosStart << " to " << cursorPosEnd << " (excluded). Parag=" << parag << " length=" << parag->length() << endl;
01782     for ( int i = cursorPosStart; i < cursorPosEnd; ++i )
01783     {
01784         KoTextStringChar & ch = *(parag->at(i));
01785         KoTextFormat * newFormat = ch.format();
01786         if( ch.isCustom() )
01787         {
01788             posEnd = i;
01789             c1.setParag( parag );
01790             c1.setIndex( posStart );
01791             c2.setParag( parag );
01792             c2.setIndex( posEnd );
01793             //kdDebug() << k_funcinfo << "found custom at " << i << " : doing from " << posStart << " to " << posEnd << endl;
01794 
01795             const QString repl = text.mid( posStart, posEnd - posStart );
01796             textdoc->setSelectionStart( KoTextDocument::Temp, &c1 );
01797             textdoc->setSelectionEnd( KoTextDocument::Temp, &c2 );
01798             macroCmd->addCommand(replaceSelectionCommand(
01799                                      cursor, textChangedCase(repl,_type),
01800                                      KoTextDocument::Temp, QString::null, false, false /*\n isn't new parag*/ ));
01801             do
01802             {
01803                 ++i;
01804             }
01805             while( parag->at(i)->isCustom() && i != cursorPosEnd);
01806             posStart=i;
01807             posEnd=i;
01808         }
01809         else
01810         {
01811             if ( newFormat != curFormat )
01812             {
01813                 posEnd=i;
01814                 c1.setParag( parag );
01815                 c1.setIndex( posStart );
01816                 c2.setParag( parag );
01817                 c2.setIndex( posEnd );
01818                 //kdDebug() << k_funcinfo << "found new format at " << i << " : doing from " << posStart << " to " << posEnd << endl;
01819 
01820                 const QString repl = text.mid( posStart, posEnd - posStart );
01821                 textdoc->setSelectionStart( KoTextDocument::Temp, &c1 );
01822                 textdoc->setSelectionEnd( KoTextDocument::Temp, &c2 );
01823                 macroCmd->addCommand(replaceSelectionCommand(
01824                                          cursor, textChangedCase(repl,_type),
01825                                          KoTextDocument::Temp, QString::null, false, false /*\n isn't new parag*/ ));
01826                 posStart=i;
01827                 posEnd=i;
01828                 curFormat = newFormat;
01829             }
01830         }
01831     }
01832     if ( posStart != cursorPosEnd )
01833     {
01834         //kdDebug() << k_funcinfo << "finishing: doing from " << posStart << " to " << cursorPosEnd << endl;
01835         c1.setParag( parag );
01836         c1.setIndex( posStart );
01837         c2.setParag( parag );
01838         c2.setIndex( cursorPosEnd );
01839 
01840         textdoc->setSelectionStart( KoTextDocument::Temp, &c1 );
01841         textdoc->setSelectionEnd( KoTextDocument::Temp, &c2 );
01842         const QString repl = text.mid( posStart, cursorPosEnd - posStart );
01843         macroCmd->addCommand(replaceSelectionCommand(
01844                                  cursor, textChangedCase(repl,_type),
01845                                  KoTextDocument::Temp, QString::null, false, false /*\n isn't new parag*/ ));
01846     }
01847     return macroCmd;
01848 
01849 }
01850 
01851 KCommand *KoTextObject::changeCaseOfText(KoTextCursor *cursor,KoChangeCaseDia::TypeOfCase _type)
01852 {
01853     if ( protectContent() )
01854         return 0L;
01855     KMacroCommand * macroCmd = new KMacroCommand( i18n("Change Case") );
01856 
01857     KoTextCursor start = textdoc->selectionStartCursor( KoTextDocument::Standard );
01858     KoTextCursor end = textdoc->selectionEndCursor( KoTextDocument::Standard );
01859     emit hideCursor();
01860 
01861     if ( start.parag() == end.parag() )
01862     {
01863         int endIndex = QMIN( start.parag()->length() - 1, end.index() );
01864         macroCmd->addCommand( changeCaseOfTextParag( start.index(), endIndex, _type,
01865                                                      cursor, start.parag() ) );
01866     }
01867     else
01868     {
01869         macroCmd->addCommand( changeCaseOfTextParag( start.index(), start.parag()->length() - 1, _type,
01870                                                      cursor, start.parag() ) );
01871         KoTextParag *p = start.parag()->next();
01872         while ( p && p != end.parag() )
01873         {
01874             macroCmd->addCommand( changeCaseOfTextParag(0, p->length() - 1, _type, cursor, p ) );
01875             p = p->next();
01876         }
01877         int endIndex = QMIN( p->length() - 1, end.index() );
01878         macroCmd->addCommand( changeCaseOfTextParag(0, endIndex, _type, cursor, end.parag() ));
01879     }
01880     formatMore( 2 );
01881     emit repaintChanged( this );
01882     emit ensureCursorVisible();
01883     emit updateUI( true );
01884     emit showCursor();
01885     return macroCmd;
01886 }
01887 
01888 QString KoTextObject::textChangedCase(const QString& _text,KoChangeCaseDia::TypeOfCase _type)
01889 {
01890     QString text(_text);
01891     switch(_type)
01892     {
01893         case KoChangeCaseDia::UpperCase:
01894             text=text.upper();
01895             break;
01896         case KoChangeCaseDia::LowerCase:
01897             text=text.lower();
01898             break;
01899         case KoChangeCaseDia::TitleCase:
01900             for(uint i=0;i<text.length();i++)
01901             {
01902                 if(text.at(i)!=' ')
01903                 {
01904                     QChar prev = text.at(QMAX(i-1,0));
01905                     if(i==0 || prev  == ' ' || prev == '\n' || prev == '\t')
01906                         text=text.replace(i, 1, text.at(i).upper() );
01907                     else
01908                         text=text.replace(i, 1, text.at(i).lower() );
01909                 }
01910             }
01911             break;
01912         case KoChangeCaseDia::ToggleCase:
01913             for(uint i=0;i<text.length();i++)
01914             {
01915                 QString repl=QString(text.at(i));
01916                 if(text.at(i)!=text.at(i).upper())
01917                     repl=repl.upper();
01918                 else if(text.at(i).lower()!=text.at(i))
01919                     repl=repl.lower();
01920                 text=text.replace(i, 1, repl );
01921             }
01922             break;
01923         case KoChangeCaseDia::SentenceCase:
01924             for(uint i=0;i<text.length();i++)
01925             {
01926                 if(text.at(i)!=' ')
01927                 {
01928                     QChar prev = text.at(QMAX(i-1,0));
01929                     if(i==0 || prev == '\n' ||prev.isPunct())
01930                         text=text.replace(i, 1, text.at(i).upper() );
01931                 }
01932             }
01933             break;
01934         default:
01935             kdDebug(32500)<<"Error in changeCaseOfText !\n";
01936             break;
01937 
01938     }
01939     return text;
01940 }
01941 
01942 // Warning, this doesn't ref the format!
01943 KoTextFormat * KoTextObject::currentFormat() const
01944 {
01945     // We use the formatting of the very first character
01946     // Should we use a style instead, maybe ?
01947     KoTextStringChar *ch = textdoc->firstParag()->at( 0 );
01948     return ch->format();
01949 }
01950 
01951 const KoParagLayout * KoTextObject::currentParagLayoutFormat() const
01952 {
01953     KoTextParag * parag = textdoc->firstParag();
01954     return &(parag->paragLayout());
01955 }
01956 
01957 bool KoTextObject::rtl() const
01958 {
01959     return textdoc->firstParag()->string()->isRightToLeft();
01960 }
01961 
01962 void KoTextObject::loadOasisContent( const QDomElement &bodyElem, KoOasisContext& context, KoStyleCollection * styleColl )
01963 {
01964     textDocument()->clear(false); // Get rid of dummy paragraph (and more if any)
01965     setLastFormattedParag( 0L ); // no more parags, avoid UMR in next setLastFormattedParag call
01966 
01967     KoTextParag *lastParagraph = textDocument()->loadOasisText( bodyElem, context, 0, styleColl, 0 );
01968 
01969     if ( !lastParagraph )                // We created no paragraph
01970     {
01971         // Create an empty one, then. See KoTextDocument ctor.
01972         textDocument()->clear( true );
01973         textDocument()->firstParag()->setStyle( styleColl->findStyle( "Standard" ) );
01974     }
01975     else
01976         textDocument()->setLastParag( lastParagraph );
01977 
01978     setLastFormattedParag( textDocument()->firstParag() );
01979 }
01980 
01981 KoTextCursor KoTextObject::pasteOasisText( const QDomElement &bodyElem, KoOasisContext& context,
01982                                            KoTextCursor& cursor, KoStyleCollection * styleColl )
01983 {
01984     KoTextCursor resultCursor( cursor );
01985     KoTextParag* lastParagraph = cursor.parag();
01986     bool removeNewline = false;
01987     uint pos = cursor.index();
01988     if ( pos == 0 && lastParagraph->length() <= 1 ) {
01989         // Pasting on an empty paragraph -> respect <text:h> in selected text etc.
01990         lastParagraph = lastParagraph->prev();
01991         lastParagraph = textDocument()->loadOasisText( bodyElem, context, lastParagraph, styleColl, cursor.parag() );
01992         if ( lastParagraph ) {
01993             resultCursor.setParag( lastParagraph );
01994             resultCursor.setIndex( lastParagraph->length() - 1 );
01995         }
01996         removeNewline = true;
01997     } else {
01998         // Pasting inside a non-empty paragraph -> insert/append text to it.
01999         // This loop looks for the *FIRST* paragraph only.
02000         for ( QDomNode text (bodyElem.firstChild()); !text.isNull(); text = text.nextSibling() )
02001         {
02002             QDomElement tag = text.toElement();
02003             if ( tag.isNull() ) continue;
02004             context.styleStack().save();
02005             // The first tag could be a text:p, text:h, text:numbered-paragraph, but also
02006             // a frame (anchored to page), a TOC, etc. For those, don't try to concat-with-existing-paragraph.
02007             // For text:numbered-paragraph, find the text:p or text:h inside it.
02008             QDomElement tagToLoad = tag;
02009             if ( tag.localName() == "numbered-paragraph" ) {
02010                 QDomElement npchild;
02011                 forEachElement( npchild, tag )
02012                 {
02013                     if ( npchild.localName() == "p" || npchild.localName() == "h" ) {
02014                         tagToLoad = npchild;
02015                         break;
02016                     }
02017                 }
02018 
02019             }
02020             if ( tagToLoad.localName() == "p" || tagToLoad.localName() == "h" ) {
02021                 context.fillStyleStack( tagToLoad, KoXmlNS::text, "style-name" );
02022                 lastParagraph->loadOasisSpan( tagToLoad, context, pos );
02023                 lastParagraph->setChanged( true );
02024                 lastParagraph->invalidate( 0 );
02025                 context.styleStack().restore();
02026                 // Done with first parag, remove it and exit loop
02027                 const_cast<QDomElement &>( bodyElem ).removeChild( tag ); // somewhat hackish
02028             }
02029             break;
02030         }
02031         resultCursor.setParag( lastParagraph );
02032         resultCursor.setIndex( pos );
02033         // Load the rest the usual way.
02034         lastParagraph = textDocument()->loadOasisText( bodyElem, context, lastParagraph, styleColl, lastParagraph->next() );
02035         if ( lastParagraph != resultCursor.parag() ) // we loaded more paragraphs
02036         {
02037             removeNewline = true;
02038             resultCursor.setParag( lastParagraph );
02039             resultCursor.setIndex( lastParagraph->length() - 1 );
02040         }
02041     }
02042     KoTextParag* p = resultCursor.parag();
02043     if ( p ) p = p->next();
02044     // Remove the additional newline that loadOasisText inserted
02045     if ( removeNewline && resultCursor.remove() ) {
02046         if ( m_lastFormatted == p ) { // has been deleted
02047             m_lastFormatted = resultCursor.parag();
02048         }
02049     }
02050     return resultCursor;
02051 }
02052 
02053 void KoTextObject::saveOasisContent( KoXmlWriter& writer, KoSavingContext& context ) const
02054 {
02055     textDocument()->saveOasisContent( writer, context );
02056 }
02057 
02058 KCommand *KoTextObject::setParagLayoutFormatCommand( KoParagLayout *newLayout,int flags,int marginIndex)
02059 {
02060     if ( protectContent() )
02061         return 0L;
02062     textdoc->selectAll( KoTextDocument::Temp );
02063     KoTextCursor *cursor = new KoTextCursor( textdoc );
02064     KCommand* cmd = setParagLayoutCommand( cursor, *newLayout, KoTextDocument::Temp,
02065                                            flags, marginIndex, true /*createUndoRedo*/ );
02066     textdoc->removeSelection( KoTextDocument::Temp );
02067     delete cursor;
02068     return cmd;
02069 }
02070 
02071 void KoTextObject::setFormat( KoTextFormat * newFormat, int flags, bool zoomFont )
02072 {
02073     if ( protectContent() )
02074         return ;
02075     // This version of setFormat works on the whole textobject - we use the Temp selection for that
02076     textdoc->selectAll( KoTextDocument::Temp );
02077     KCommand *cmd = setFormatCommand( 0L, 0L, newFormat,
02078                                       flags, zoomFont, KoTextDocument::Temp );
02079     textdoc->removeSelection( KoTextDocument::Temp );
02080     if (cmd)
02081         emit newCommand( cmd );
02082 
02083     KoTextFormat format = *currentFormat();
02084     //format.setPointSize( docFontSize( currentFormat() ) ); // "unzoom" the font size
02085     emit showFormatObject(format);
02086 }
02087 
02088 KCommand *KoTextObject::setChangeCaseOfTextCommand(KoChangeCaseDia::TypeOfCase _type)
02089 {
02090     if ( protectContent() )
02091         return 0L;
02092     textdoc->selectAll( KoTextDocument::Standard );
02093     KoTextCursor *cursor = new KoTextCursor( textdoc );
02094     KCommand* cmd = changeCaseOfText(cursor, _type);
02095     textdoc->removeSelection( KoTextDocument::Standard );
02096     delete cursor;
02097     return cmd;
02098 }
02099 
02100 void KoTextObject::setNeedSpellCheck(bool b)
02101 {
02102     m_needsSpellCheck = b;
02103     for (KoTextParag * parag = textdoc->firstParag(); parag ; parag = parag->next())
02104         parag->string()->setNeedsSpellCheck( b );
02105 }
02106 
02107 bool KoTextObject::statistics( QProgressDialog *progress, ulong & charsWithSpace, ulong & charsWithoutSpace, ulong & words, ulong & sentences, ulong & syllables, ulong & lines, bool selected )
02108 {
02109     // parts of words for better counting of syllables:
02110     // (only use reg exp if necessary -> speed up)
02111 
02112     QStringList subs_syl;
02113     subs_syl << "cial" << "tia" << "cius" << "cious" << "giu" << "ion" << "iou";
02114     QStringList subs_syl_regexp;
02115     subs_syl_regexp << "sia$" << "ely$";
02116 
02117     QStringList add_syl;
02118     add_syl << "ia" << "riet" << "dien" << "iu" << "io" << "ii";
02119     QStringList add_syl_regexp;
02120     add_syl_regexp << "[aeiouym]bl$" << "[aeiou]{3}" << "^mc" << "ism$"
02121         << "[^l]lien" << "^coa[dglx]." << "[^gq]ua[^auieo]" << "dnt$";
02122 
02123     QString s;
02124     KoTextParag * parag = textdoc->firstParag();
02125     for ( ; parag ; parag = parag->next() )
02126     {
02127         if (  progress )
02128         {
02129             progress->setProgress(progress->progress()+1);
02130             // MA: resizing if KWStatisticsDialog does not work correct with this enabled, don't know why
02131             kapp->processEvents();
02132             if ( progress->wasCancelled() )
02133                 return false;
02134         }
02135         // start of a table
02136 /*        if ( parag->at(0)->isCustom())
02137         {
02138             KoLinkVariable *var=dynamic_cast<KoLinkVariable *>(parag->at(0)->customItem());
02139             if(!var)
02140                 continue;
02141                 }*/
02142         bool hasTrailingSpace = true;
02143         if ( !selected ) {
02144             s = parag->string()->toString();
02145             lines += parag->lines();
02146         } else {
02147             if ( parag->hasSelection( KoTextDocument::Standard ) ) {
02148                 hasTrailingSpace = false;
02149                 s = parag->string()->toString();
02150                 if ( !( parag->fullSelected( KoTextDocument::Standard ) ) ) {
02151                     s = s.mid( parag->selectionStart( KoTextDocument::Standard ), parag->selectionEnd( KoTextDocument::Standard ) - parag->selectionStart( KoTextDocument::Standard ) );
02152                     lines+=numberOfparagraphLineSelected(parag);
02153                 }
02154                 else
02155                     lines += parag->lines();
02156             } else {
02157                 continue;
02158             }
02159         }
02160 
02161         // Character count
02162         for ( uint i = 0 ; i < s.length() - ( hasTrailingSpace ? 1 : 0 ) ; ++i )
02163         {
02164             QChar ch = s[i];
02165             ++charsWithSpace;
02166             if ( !ch.isSpace() )
02167                 ++charsWithoutSpace;
02168         }
02169 
02170         // Syllable and Word count
02171         // Algorithm mostly taken from Greg Fast's Lingua::EN::Syllable module for Perl.
02172         // This guesses correct for 70-90% of English words, but the overall value
02173         // is quite good, as some words get a number that's too high and others get
02174         // one that's too low.
02175         // IMPORTANT: please test any changes against demos/statistics.kwd
02176         QRegExp re("\\s+");
02177         QStringList wordlist = QStringList::split(re, s);
02178         words += wordlist.count();
02179         re.setCaseSensitive(false);
02180         for ( QStringList::Iterator it = wordlist.begin(); it != wordlist.end(); ++it ) {
02181             QString word = *it;
02182             re.setPattern("[!?.,:_\"-]");    // clean word from punctuation
02183             word.remove(re);
02184             if ( word.length() <= 3 ) {  // extension to the original algorithm
02185                 syllables++;
02186                 continue;
02187             }
02188             re.setPattern("e$");
02189             word.remove(re);
02190             re.setPattern("[^aeiouy]+");
02191             QStringList syls = QStringList::split(re, word);
02192             int word_syllables = 0;
02193             for ( QStringList::Iterator it = subs_syl.begin(); it != subs_syl.end(); ++it ) {
02194                 if( word.find(*it, 0, false) != -1 )
02195                     word_syllables--;
02196             }
02197             for ( QStringList::Iterator it = subs_syl_regexp.begin(); it != subs_syl_regexp.end(); ++it ) {
02198                 re.setPattern(*it);
02199                 if( word.find(re) != -1 )
02200                     word_syllables--;
02201             }
02202             for ( QStringList::Iterator it = add_syl.begin(); it != add_syl.end(); ++it ) {
02203                 if( word.find(*it, 0, false) != -1 )
02204                     word_syllables++;
02205             }
02206             for ( QStringList::Iterator it = add_syl_regexp.begin(); it != add_syl_regexp.end(); ++it ) {
02207                 re.setPattern(*it);
02208                 if( word.find(re) != -1 )
02209                     word_syllables++;
02210             }
02211             word_syllables += syls.count();
02212             if ( word_syllables == 0 )
02213                 word_syllables = 1;
02214             syllables += word_syllables;
02215         }
02216         re.setCaseSensitive(true);
02217 
02218         // Sentence count
02219         // Clean up for better result, destroys the original text but we only want to count
02220         s = s.stripWhiteSpace();
02221         QChar lastchar = s.at(s.length());
02222         if( ! s.isEmpty() && ! KoAutoFormat::isMark( lastchar ) ) {  // e.g. for headlines
02223             s = s + ".";
02224         }
02225         re.setPattern("[.?!]+");         // count "..." as only one "."
02226         s.replace(re, ".");
02227         re.setPattern("\\d\\.\\d");      // don't count floating point numbers as sentences
02228         s.replace(re, "0,0");
02229         re.setPattern("[A-Z]\\.+");      // don't count "U.S.A." as three sentences
02230         s.replace(re, "*");
02231         for ( uint i = 0 ; i < s.length() ; ++i )
02232         {
02233             QChar ch = s[i];
02234             if ( KoAutoFormat::isMark( ch ) )
02235                 ++sentences;
02236         }
02237     }
02238     return true;
02239 }
02240 
02241 int KoTextObject::numberOfparagraphLineSelected( KoTextParag *parag)
02242 {
02243     int indexOfLineStart;
02244     int lineStart;
02245     int lineEnd;
02246     KoTextCursor c1 = textdoc->selectionStartCursor( KoTextDocument::Standard );
02247     KoTextCursor c2 = textdoc->selectionEndCursor( KoTextDocument::Standard );
02248     parag->lineStartOfChar( c1.index(), &indexOfLineStart, &lineStart );
02249 
02250     parag->lineStartOfChar( c2.index(), &indexOfLineStart, &lineEnd );
02251     return (lineEnd - lineStart+1);
02252 }
02253 
02254 KoVariable* KoTextObject::variableAtPoint( const QPoint& iPoint ) const
02255 {
02256     KoTextCursor cursor( textDocument() );
02257     int variablePosition = -1;
02258     cursor.place( iPoint, textDocument()->firstParag(), false, &variablePosition );
02259     if ( variablePosition == -1 )
02260         return 0;
02261     return variableAtPosition( cursor.parag(), variablePosition );
02262 }
02263 
02264 KoVariable* KoTextObject::variableAtPosition( KoTextParag* parag, int index ) const
02265 {
02266     KoTextStringChar * ch = parag->at( index );
02267     if ( ch->isCustom() )
02268         return dynamic_cast<KoVariable *>( ch->customItem() );
02269     return 0;
02270 }
02271 
02272 const char * KoTextObject::acceptSelectionMimeType()
02273 {
02274     return "application/vnd.oasis.opendocument.";
02275 }
02276 
02277 QCString KoTextObject::providesOasis( QMimeSource* mime )
02278 {
02279     const char* fmt;
02280     const char* acceptMimeType = acceptSelectionMimeType();
02281     for ( int i = 0; (fmt = mime->format(i)); ++i )
02282     {
02283     if ( QString( fmt ).startsWith( acceptMimeType ) )
02284         {
02285             return fmt;
02286         }
02287     }
02288     return "";
02289 }
02290 
02291 #ifndef NDEBUG
02292 void KoTextObject::printRTDebug(int info)
02293 {
02294     KoTextParag* lastParag = 0;
02295     for (KoTextParag * parag = textdoc->firstParag(); parag ; parag = parag->next())
02296     {
02297         assert( parag->prev() == lastParag );
02298         parag->printRTDebug( info );
02299         lastParag = parag;
02300     }
02301     if ( info == 1 )
02302         textdoc->formatCollection()->debug();
02303 }
02304 #endif
02305 
02307 
02308 KCommand *KoTextFormatInterface::setBoldCommand(bool on)
02309 {
02310     KoTextFormat format( *currentFormat() );
02311     format.setBold( on );
02312     return setFormatCommand( &format, KoTextFormat::Bold );
02313 }
02314 
02315 KCommand *KoTextFormatInterface::setItalicCommand( bool on)
02316 {
02317     KoTextFormat format( *currentFormat() );
02318     format.setItalic( on );
02319     return setFormatCommand( &format, KoTextFormat::Italic );
02320 }
02321 
02322 KCommand *KoTextFormatInterface::setUnderlineCommand( bool on )
02323 {
02324     KoTextFormat format( *currentFormat() );
02325     format.setUnderlineType( on ? KoTextFormat::U_SIMPLE : KoTextFormat::U_NONE);
02326     return setFormatCommand( &format, KoTextFormat::ExtendUnderLine );
02327 }
02328 
02329 KCommand *KoTextFormatInterface::setUnderlineColorCommand( const QColor &color )
02330 {
02331     KoTextFormat format( *currentFormat() );
02332     format.setTextUnderlineColor( color);
02333     return setFormatCommand( &format, KoTextFormat::ExtendUnderLine );
02334 }
02335 
02336 KCommand *KoTextFormatInterface::setDoubleUnderlineCommand( bool on )
02337 {
02338     KoTextFormat format( *currentFormat() );
02339     format.setUnderlineType( on ? KoTextFormat::U_DOUBLE : KoTextFormat::U_NONE);
02340     return setFormatCommand( &format, KoTextFormat::ExtendUnderLine );
02341 }
02342 
02343 KCommand *KoTextFormatInterface::setStrikeOutCommand( bool on )
02344 {
02345     KoTextFormat format( *currentFormat() );
02346     format.setStrikeOutType( on ? KoTextFormat::S_SIMPLE : KoTextFormat::S_NONE);
02347     return setFormatCommand( &format, KoTextFormat::StrikeOut );
02348 }
02349 
02350 KCommand *KoTextFormatInterface::setTextBackgroundColorCommand(const QColor & col)
02351 {
02352     KoTextFormat format( *currentFormat() );
02353     format.setTextBackgroundColor( col );
02354     return setFormatCommand( &format, KoTextFormat::TextBackgroundColor );
02355 }
02356 
02357 KCommand *KoTextFormatInterface::setPointSizeCommand( int s )
02358 {
02359     KoTextFormat format( *currentFormat() );
02360     format.setPointSize( s );
02361     return setFormatCommand( &format, KoTextFormat::Size, true /* zoom the font size */ );
02362 }
02363 
02364 KCommand *KoTextFormatInterface::setFamilyCommand(const QString &font)
02365 {
02366     KoTextFormat format( *currentFormat() );
02367     format.setFamily( font );
02368     return setFormatCommand( &format, KoTextFormat::Family );
02369 }
02370 
02371 QColor KoTextFormatInterface::textBackgroundColor() const
02372 {
02373     return currentFormat()->textBackgroundColor();
02374 }
02375 
02376 QColor KoTextFormatInterface::textUnderlineColor()const
02377 {
02378     return currentFormat()->textUnderlineColor();
02379 }
02380 
02381 QColor KoTextFormatInterface::textColor() const
02382 {
02383     return currentFormat()->color();
02384 }
02385 
02386 bool KoTextFormatInterface::textUnderline()const
02387 {
02388     return currentFormat()->underline();
02389 }
02390 
02391 bool KoTextFormatInterface::textDoubleUnderline()const
02392 {
02393     return currentFormat()->doubleUnderline();
02394 }
02395 
02396 bool KoTextFormatInterface::textBold()const
02397 {
02398     return currentFormat()->font().bold();
02399 }
02400 
02401 bool KoTextFormatInterface::textStrikeOut()const
02402 {
02403     return currentFormat()->font().strikeOut();
02404 }
02405 
02406 bool KoTextFormatInterface::textItalic() const
02407 {
02408     return currentFormat()->font().italic();
02409 }
02410 
02411 bool KoTextFormatInterface::textSubScript() const
02412 {
02413     return (currentFormat()->vAlign()==KoTextFormat::AlignSubScript);
02414 }
02415 
02416 bool KoTextFormatInterface::textSuperScript() const
02417 {
02418     return (currentFormat()->vAlign()==KoTextFormat::AlignSuperScript);
02419 }
02420 
02421 double KoTextFormatInterface::shadowDistanceX() const
02422 {
02423     return currentFormat()->shadowDistanceX();
02424 }
02425 
02426 double KoTextFormatInterface::shadowDistanceY() const
02427 {
02428     return currentFormat()->shadowDistanceY();
02429 }
02430 
02431 QColor KoTextFormatInterface::shadowColor() const
02432 {
02433     return currentFormat()->shadowColor();
02434 }
02435 
02436 KoTextFormat::AttributeStyle KoTextFormatInterface::fontAttribute() const
02437 {
02438     return currentFormat()->attributeFont();
02439 }
02440 
02441 double KoTextFormatInterface::relativeTextSize() const
02442 {
02443     return currentFormat()->relativeTextSize();
02444 }
02445 
02446 int KoTextFormatInterface::offsetFromBaseLine()const
02447 {
02448     return currentFormat()->offsetFromBaseLine();
02449 }
02450 
02451 bool KoTextFormatInterface::wordByWord()const
02452 {
02453     return currentFormat()->wordByWord();
02454 }
02455 
02456 bool KoTextFormatInterface::hyphenation()const
02457 {
02458     return currentFormat()->hyphenation();
02459 }
02460 
02461 KoTextFormat::UnderlineType KoTextFormatInterface::underlineType()const
02462 {
02463     return currentFormat()->underlineType();
02464 }
02465 
02466 KoTextFormat::StrikeOutType KoTextFormatInterface::strikeOutType()const
02467 {
02468     return currentFormat()->strikeOutType();
02469 }
02470 
02471 KoTextFormat::UnderlineStyle KoTextFormatInterface::underlineStyle()const
02472 {
02473     return currentFormat()->underlineStyle();
02474 }
02475 
02476 KoTextFormat::StrikeOutStyle KoTextFormatInterface::strikeOutStyle()const
02477 {
02478     return currentFormat()->strikeOutStyle();
02479 }
02480 
02481 QFont KoTextFormatInterface::textFont() const
02482 {
02483     QFont fn( currentFormat()->font() );
02484     // "unzoom" the font size
02485     //fn.setPointSize( static_cast<int>( KoTextZoomHandler::layoutUnitPtToPt( fn.pointSize() ) ) );
02486     return fn;
02487 }
02488 
02489 QString KoTextFormatInterface::textFontFamily()const
02490 {
02491     return currentFormat()->font().family();
02492 }
02493 
02494 QString KoTextFormatInterface::language() const
02495 {
02496     return currentFormat()->language();
02497 }
02498 
02499 KCommand *KoTextFormatInterface::setTextColorCommand(const QColor &color)
02500 {
02501     KoTextFormat format( *currentFormat() );
02502     format.setColor( color );
02503     return setFormatCommand( &format, KoTextFormat::Color );
02504 }
02505 
02506 KCommand *KoTextFormatInterface::setTextSubScriptCommand(bool on)
02507 {
02508     KoTextFormat format( *currentFormat() );
02509     if(!on)
02510         format.setVAlign(KoTextFormat::AlignNormal);
02511     else
02512         format.setVAlign(KoTextFormat::AlignSubScript);
02513     return setFormatCommand( &format, KoTextFormat::VAlign );
02514 }
02515 
02516 KCommand *KoTextFormatInterface::setTextSuperScriptCommand(bool on)
02517 {
02518     KoTextFormat format( *currentFormat() );
02519     if(!on)
02520         format.setVAlign(KoTextFormat::AlignNormal);
02521     else
02522         format.setVAlign(KoTextFormat::AlignSuperScript);
02523     return setFormatCommand( &format, KoTextFormat::VAlign );
02524 }
02525 
02526 KCommand *KoTextFormatInterface::setDefaultFormatCommand()
02527 {
02528     KoTextFormatCollection * coll = currentFormat()->parent();
02529     Q_ASSERT(coll);
02530     if(coll)
02531     {
02532         KoTextFormat * format = coll->defaultFormat();
02533         return setFormatCommand( format, KoTextFormat::Format );
02534     } else {
02535         kdDebug() << "useless call to setDefaultFormatCommand at: " << kdBacktrace() << endl;
02536     }
02537     return 0;
02538 }
02539 
02540 KCommand *KoTextFormatInterface::setAlignCommand(int align)
02541 {
02542     KoParagLayout format( *currentParagLayoutFormat() );
02543     format.alignment=align;
02544     return setParagLayoutFormatCommand(&format,KoParagLayout::Alignment);
02545 }
02546 
02547 KCommand *KoTextFormatInterface::setHyphenationCommand( bool _b )
02548 {
02549     KoTextFormat format( *currentFormat() );
02550     format.setHyphenation( _b );
02551     return setFormatCommand( &format, KoTextFormat::Hyphenation);
02552 }
02553 
02554 
02555 KCommand *KoTextFormatInterface::setShadowTextCommand( double shadowDistanceX, double shadowDistanceY, const QColor& shadowColor )
02556 {
02557     KoTextFormat format( *currentFormat() );
02558     format.setShadow( shadowDistanceX, shadowDistanceY, shadowColor );
02559     return setFormatCommand( &format, KoTextFormat::ShadowText );
02560 }
02561 
02562 KCommand *KoTextFormatInterface::setFontAttributeCommand( KoTextFormat::AttributeStyle _att)
02563 {
02564     KoTextFormat format( *currentFormat() );
02565     format.setAttributeFont( _att );
02566     return setFormatCommand( &format, KoTextFormat::Attribute );
02567 }
02568 
02569 KCommand *KoTextFormatInterface::setRelativeTextSizeCommand( double _size )
02570 {
02571     KoTextFormat format( *currentFormat() );
02572     format.setRelativeTextSize( _size );
02573     return setFormatCommand( &format, KoTextFormat::VAlign );
02574 }
02575 
02576 KCommand *KoTextFormatInterface::setOffsetFromBaseLineCommand( int _offset )
02577 {
02578     KoTextFormat format( *currentFormat() );
02579     format.setOffsetFromBaseLine( _offset );
02580     return setFormatCommand( &format, KoTextFormat::OffsetFromBaseLine );
02581 }
02582 
02583 KCommand *KoTextFormatInterface::setWordByWordCommand( bool _b )
02584 {
02585     KoTextFormat format( *currentFormat() );
02586     format.setWordByWord( _b );
02587     return setFormatCommand( &format, KoTextFormat::WordByWord );
02588 }
02589 
02590 #if 0
02591 void KoTextFormatInterface::setAlign(int align)
02592 {
02593     KCommand *cmd = setAlignCommand( align );
02594     emitNewCommand( cmd );
02595 }
02596 
02597 void KoTextFormatInterface::setMargin(QStyleSheetItem::Margin m, double margin)
02598 {
02599     KCommand *cmd = setMarginCommand( m, margin );
02600     emitNewCommand( cmd );
02601 }
02602 
02603 void KoTextFormatInterface::setTabList(const KoTabulatorList & tabList )
02604 {
02605     KCommand *cmd = setTabListCommand( tabList );
02606     emitNewCommand( cmd );
02607 }
02608 
02609 void KoTextFormatInterface::setCounter(const KoParagCounter & counter )
02610 {
02611     KCommand *cmd = setCounterCommand( counter );
02612     emitNewCommand( cmd );
02613 }
02614 
02615 void KoTextFormatInterface::setParagLayoutFormat( KoParagLayout *newLayout, int flags, int marginIndex)
02616 {
02617     KCommand *cmd = setParagLayoutFormatCommand(newLayout, flags, marginIndex);
02618     emitNewCommand( cmd );
02619 }
02620 #endif
02621 
02622 KCommand *KoTextFormatInterface::setMarginCommand(QStyleSheetItem::Margin m, double margin)
02623 {
02624     KoParagLayout format( *currentParagLayoutFormat() );
02625     format.margins[m]=margin;
02626     return setParagLayoutFormatCommand(&format,KoParagLayout::Margins,(int)m);
02627 }
02628 
02629 KCommand *KoTextFormatInterface::setTabListCommand(const KoTabulatorList & tabList )
02630  {
02631     KoParagLayout format( *currentParagLayoutFormat() );
02632     format.setTabList(tabList);
02633     return setParagLayoutFormatCommand(&format,KoParagLayout::Tabulator);
02634 }
02635 
02636 KCommand *KoTextFormatInterface::setCounterCommand(const KoParagCounter & counter )
02637 {
02638     KoParagLayout format( *currentParagLayoutFormat() );
02639     if(!format.counter)
02640         format.counter = new KoParagCounter;
02641     *format.counter = counter;
02642     return setParagLayoutFormatCommand(&format,KoParagLayout::BulletNumber);
02643 }
02644 
02645 KCommand *KoTextFormatInterface::setLanguageCommand(const QString &_lang)
02646 {
02647     KoTextFormat format( *currentFormat() );
02648     format.setLanguage(_lang);
02649     return setFormatCommand( &format, KoTextFormat::Language );
02650 }
02651 
02652 KoTextDocCommand *KoTextFormatInterface::deleteTextCommand( KoTextDocument *textdoc, int id, int index, const QMemArray<KoTextStringChar> & str, const CustomItemsMap & customItemsMap, const QValueList<KoParagLayout> & oldParagLayouts )
02653 {
02654     return textdoc->deleteTextCommand( textdoc, id, index, str, customItemsMap, oldParagLayouts );
02655 }
02656 
02657 #include "kotextobject.moc"
KDE Logo
This file is part of the documentation for lib Library Version 1.4.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Feb 13 09:40:10 2006 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003