lib

KoTextObject.cpp

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