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