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