diff options
Diffstat (limited to 'lib/kotext/KoTextObject.cpp')
-rw-r--r-- | lib/kotext/KoTextObject.cpp | 2809 |
1 files changed, 2809 insertions, 0 deletions
diff --git a/lib/kotext/KoTextObject.cpp b/lib/kotext/KoTextObject.cpp new file mode 100644 index 00000000..3ff03216 --- /dev/null +++ b/lib/kotext/KoTextObject.cpp @@ -0,0 +1,2809 @@ +/* This file is part of the KDE project + Copyright (C) 2001-2006 David Faure <faure@kde.org> + Copyright (C) 2005 Martin Ellis <martin.ellis@kdemail.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "KoTextObject.h" +#include "KoTextParag.h" +#include "KoParagCounter.h" +#include "KoTextZoomHandler.h" +#include "KoTextCommand.h" +#include "KoStyleCollection.h" +#include "KoFontDia.h" +#include "KoOasisContext.h" +#include "KoVariable.h" +#include "KoAutoFormat.h" +#include <KoXmlNS.h> +#include <KoDom.h> + +#include <klocale.h> +#include <kdebug.h> +#include <kapplication.h> + +#include <qtimer.h> +#include <qregexp.h> +#include <qprogressdialog.h> + +#include <assert.h> + +//#define DEBUG_FORMATS +//#define DEBUG_FORMAT_MORE + +const char KoTextObject::s_customItemChar = '#'; // Has to be transparent to kspell but still be saved (not space) + +struct KoTextObject::KoTextObjectPrivate +{ +public: + KoTextObjectPrivate() { + afterFormattingEmitted = false; + abortFormatting = false; + } + bool afterFormattingEmitted; + bool abortFormatting; +}; + +KoTextObject::KoTextObject( KoTextZoomHandler *zh, const QFont& defaultFont, + const QString &defaultLanguage, bool hyphenation, + KoParagStyle* defaultStyle, int tabStopWidth, + QObject* parent, const char *name ) + : QObject( parent, name ), m_defaultStyle( defaultStyle ), undoRedoInfo( this ) +{ + textdoc = new KoTextDocument( zh, new KoTextFormatCollection( defaultFont, QColor(),defaultLanguage, hyphenation ) ); + if ( tabStopWidth != -1 ) + textdoc->setTabStops( tabStopWidth ); + init(); +} + +KoTextObject::KoTextObject( KoTextDocument* _textdoc, KoParagStyle* defaultStyle, + QObject* parent, const char *name ) + : QObject( parent, name ), m_defaultStyle( defaultStyle ), undoRedoInfo( this ) +{ + textdoc = _textdoc; + init(); +} + +void KoTextObject::init() +{ + d = new KoTextObjectPrivate; + m_needsSpellCheck = true; + m_protectContent = false; + m_visible=true; + m_availableHeight = -1; + m_lastFormatted = textdoc->firstParag(); + m_highlightSelectionAdded = false; + interval = 0; + changeIntervalTimer = new QTimer( this ); + connect( changeIntervalTimer, SIGNAL( timeout() ), + this, SLOT( doChangeInterval() ) ); + + formatTimer = new QTimer( this ); + connect( formatTimer, SIGNAL( timeout() ), + this, SLOT( formatMore() ) ); + + // Apply default style to initial paragraph + if ( m_lastFormatted && m_defaultStyle ) + m_lastFormatted->applyStyle( m_defaultStyle ); + + connect( textdoc, SIGNAL( paragraphDeleted( KoTextParag* ) ), + this, SIGNAL( paragraphDeleted( KoTextParag* ) ) ); + connect( textdoc, SIGNAL( paragraphDeleted( KoTextParag* ) ), + this, SLOT( slotParagraphDeleted( KoTextParag* ) ) ); + connect( textdoc, SIGNAL( newCommand( KCommand* ) ), + this, SIGNAL( newCommand( KCommand* ) ) ); + connect( textdoc, SIGNAL( repaintChanged() ), + this, SLOT( emitRepaintChanged() ) ); + + connect( this, SIGNAL(paragraphModified( KoTextParag*, int, int , int ) ), + this, SLOT(slotParagraphModified(KoTextParag *, int, int , int))); + connect( this, SIGNAL(paragraphCreated( KoTextParag* )), + this, SLOT(slotParagraphCreated(KoTextParag *))); +} + +KoTextObject::~KoTextObject() +{ + // Avoid crash in KoTextString::clear -> accessing deleted format collection, + // if ~UndoRedoInfo still has a string to clear. + undoRedoInfo.clear(); + delete textdoc; textdoc = 0; + delete d; +} + +int KoTextObject::availableHeight() const +{ + if ( m_availableHeight == -1 ) + emit const_cast<KoTextObject *>(this)->availableHeightNeeded(); + Q_ASSERT( m_availableHeight != -1 ); + return m_availableHeight; +} + +void KoTextObject::slotParagraphModified(KoTextParag * /*parag*/, int /*ParagModifyType*/ _type, int , int) +{ + if ( _type == ChangeFormat) + return; + m_needsSpellCheck = true; +} + +void KoTextObject::slotParagraphCreated(KoTextParag * /*parag*/) +{ + m_needsSpellCheck = true; +} + +void KoTextObject::slotParagraphDeleted(KoTextParag * parag) +{ + if ( m_lastFormatted == parag ) + m_lastFormatted = parag->next(); + + // ### TODO: remove from kwbgspellcheck + // not needed, since KoTextIterator takes care of that. +} + +int KoTextObject::docFontSize( KoTextFormat * format ) const +{ + Q_ASSERT( format ); + return format->pointSize(); +} + +int KoTextObject::zoomedFontSize( int docFontSize ) const +{ + kdDebug(32500) << "KoTextObject::zoomedFontSize: docFontSize=" << docFontSize + << " - in LU: " << KoTextZoomHandler::ptToLayoutUnitPt( docFontSize ) << endl; + return KoTextZoomHandler::ptToLayoutUnitPt( docFontSize ); +} + +// A visitor that looks for custom items in e.g. a selection +class KoHasCustomItemVisitor : public KoParagVisitor +{ +public: + KoHasCustomItemVisitor() : KoParagVisitor() { } + // returns false when cancelled, i.e. an item was _found_, and true if it proceeded to the end(!) + virtual bool visit( KoTextParag *parag, int start, int end ) + { + for ( int i = start ; i < end ; ++i ) + { + KoTextStringChar * ch = parag->at( i ); + if ( ch->isCustom() ) + return false; // found one -> stop here + } + return true; + } +}; + +bool KoTextObject::selectionHasCustomItems( KoTextDocument::SelectionId selectionId ) const +{ + KoHasCustomItemVisitor visitor; + bool noneFound = textdoc->visitSelection( selectionId, &visitor ); + return !noneFound; +} + +void KoTextObject::slotAfterUndoRedo() +{ + formatMore( 2 ); + emit repaintChanged( this ); + emit updateUI( true ); + emit showCursor(); + emit ensureCursorVisible(); +} + +void KoTextObject::clearUndoRedoInfo() +{ + undoRedoInfo.clear(); +} + + +void KoTextObject::checkUndoRedoInfo( KoTextCursor * cursor, UndoRedoInfo::Type t ) +{ + if ( undoRedoInfo.valid() && ( t != undoRedoInfo.type || cursor != undoRedoInfo.cursor ) ) { + undoRedoInfo.clear(); + } + undoRedoInfo.type = t; + undoRedoInfo.cursor = cursor; +} + +void KoTextObject::undo() +{ + undoRedoInfo.clear(); + emit hideCursor(); + KoTextCursor *cursor = new KoTextCursor( textdoc ); // Kindof a dummy cursor + KoTextCursor *c = textdoc->undo( cursor ); + if ( !c ) { + delete cursor; + emit showCursor(); + return; + } + // We have to set this new cursor position in all views :( + // It sucks a bit for useability, but otherwise one view might still have + // a cursor inside a deleted paragraph -> crash. + emit setCursor( c ); + setLastFormattedParag( textdoc->firstParag() ); + delete cursor; + QTimer::singleShot( 0, this, SLOT( slotAfterUndoRedo() ) ); +} + +void KoTextObject::redo() +{ + undoRedoInfo.clear(); + emit hideCursor(); + KoTextCursor *cursor = new KoTextCursor( textdoc ); // Kindof a dummy cursor + KoTextCursor *c = textdoc->redo( cursor ); + if ( !c ) { + delete cursor; + emit showCursor(); + return; + } + emit setCursor( c ); // see undo + setLastFormattedParag( textdoc->firstParag() ); + delete cursor; + QTimer::singleShot( 0, this, SLOT( slotAfterUndoRedo() ) ); +} + +KoTextObject::UndoRedoInfo::UndoRedoInfo( KoTextObject *to ) + : type( Invalid ), textobj(to), cursor( 0 ) +{ + text = QString::null; + id = -1; + index = -1; + placeHolderCmd = 0L; +} + +bool KoTextObject::UndoRedoInfo::valid() const +{ + return text.length() > 0 && id >= 0 && index >= 0 && type != Invalid; +} + +void KoTextObject::UndoRedoInfo::clear() +{ + if ( valid() ) { + KoTextDocument* textdoc = textobj->textDocument(); + switch (type) { + case Insert: + case Return: + { + KoTextDocCommand * cmd = new KoTextInsertCommand( textdoc, id, index, text.rawData(), customItemsMap, oldParagLayouts ); + textdoc->addCommand( cmd ); + Q_ASSERT( placeHolderCmd ); + if ( placeHolderCmd ) // crash prevention + { + // Inserting any custom items -> macro command, to let custom items add their command + if ( !customItemsMap.isEmpty() ) + { + CustomItemsMap::Iterator it = customItemsMap.begin(); + for ( ; it != customItemsMap.end(); ++it ) + { + KoTextCustomItem * item = it.data(); + KCommand * itemCmd = item->createCommand(); + if ( itemCmd ) + placeHolderCmd->addCommand( itemCmd ); + } + placeHolderCmd->addCommand( new KoTextCommand( textobj, /*cmd, */QString::null ) ); + } + else + { + placeHolderCmd->addCommand( new KoTextCommand( textobj, /*cmd, */QString::null ) ); + } + } + } break; + case Delete: + case RemoveSelected: + { + KoTextDocCommand * cmd = textobj->deleteTextCommand( textdoc, id, index, text.rawData(), customItemsMap, oldParagLayouts ); + textdoc->addCommand( cmd ); + Q_ASSERT( placeHolderCmd ); + placeHolderCmd->addCommand( new KoTextCommand( textobj, /*cmd, */QString::null ) ); + // Deleting any custom items -> let them add their command + if ( !customItemsMap.isEmpty() ) + { + customItemsMap.deleteAll( placeHolderCmd ); + } + } break; + case Invalid: + break; + } + } + type = Invalid; + // Before Qt-3.2.0, this called KoTextString::clear(), which called resize(0) on the array, which _detached_. Tricky. + // Since Qt-3.2.0, resize(0) doesn't detach anymore -> KoTextDocDeleteCommand calls copy() itself. + text = QString::null; + id = -1; + index = -1; + oldParagLayouts.clear(); + customItemsMap.clear(); + placeHolderCmd = 0; +} + +void KoTextObject::copyCharFormatting( KoTextParag *parag, int position, int index /*in text*/, bool moveCustomItems ) +{ + KoTextStringChar * ch = parag->at( position ); + if ( ch->format() ) { + ch->format()->addRef(); + undoRedoInfo.text.at( index ).setFormat( ch->format() ); + } + if ( ch->isCustom() ) + { + kdDebug(32500) << "KoTextObject::copyCharFormatting moving custom item " << ch->customItem() << " to text's " << index << " char" << endl; + undoRedoInfo.customItemsMap.insert( index, ch->customItem() ); + // We copy the custom item to customItemsMap in all cases (see setFormat) + // We only remove from 'ch' if moveCustomItems was specified + if ( moveCustomItems ) + parag->removeCustomItem(position); + //ch->loseCustomItem(); + } +} + +// Based on QTextView::readFormats - with all code duplication moved to copyCharFormatting +void KoTextObject::readFormats( KoTextCursor &c1, KoTextCursor &c2, bool copyParagLayouts, bool moveCustomItems ) +{ + //kdDebug(32500) << "KoTextObject::readFormats moveCustomItems=" << moveCustomItems << endl; + int oldLen = undoRedoInfo.text.length(); + if ( c1.parag() == c2.parag() ) { + undoRedoInfo.text += c1.parag()->string()->toString().mid( c1.index(), c2.index() - c1.index() ); + for ( int i = c1.index(); i < c2.index(); ++i ) + copyCharFormatting( c1.parag(), i, oldLen + i - c1.index(), moveCustomItems ); + } else { + int lastIndex = oldLen; + int i; + //kdDebug(32500) << "KoTextObject::readFormats copying from " << c1.index() << " to " << c1.parag()->length()-1 << " into lastIndex=" << lastIndex << endl; + // Replace the trailing spaces with '\n'. That char carries the formatting for the trailing space. + undoRedoInfo.text += c1.parag()->string()->toString().mid( c1.index(), c1.parag()->length() - 1 - c1.index() ) + '\n'; + for ( i = c1.index(); i < c1.parag()->length(); ++i, ++lastIndex ) + copyCharFormatting( c1.parag(), i, lastIndex, moveCustomItems ); + //++lastIndex; // skip the '\n'. + KoTextParag *p = c1.parag()->next(); + while ( p && p != c2.parag() ) { + undoRedoInfo.text += p->string()->toString().left( p->length() - 1 ) + '\n'; + //kdDebug(32500) << "KoTextObject::readFormats (mid) copying from 0 to " << p->length()-1 << " into i+" << lastIndex << endl; + for ( i = 0; i < p->length(); ++i ) + copyCharFormatting( p, i, i + lastIndex, moveCustomItems ); + lastIndex += p->length(); // + 1; // skip the '\n' + //kdDebug(32500) << "KoTextObject::readFormats lastIndex now " << lastIndex << " - text is now " << undoRedoInfo.text.toString() << endl; + p = p->next(); + } + //kdDebug(32500) << "KoTextObject::readFormats copying [last] from 0 to " << c2.index() << " into i+" << lastIndex << endl; + undoRedoInfo.text += c2.parag()->string()->toString().left( c2.index() ); + for ( i = 0; i < c2.index(); ++i ) + copyCharFormatting( c2.parag(), i, i + lastIndex, moveCustomItems ); + } + + if ( copyParagLayouts ) { + KoTextParag *p = c1.parag(); + while ( p ) { + undoRedoInfo.oldParagLayouts << p->paragLayout(); + if ( p == c2.parag() ) + break; + p = p->next(); + } + } +} + +void KoTextObject::newPlaceHolderCommand( const QString & name ) +{ + Q_ASSERT( !undoRedoInfo.placeHolderCmd ); + if ( undoRedoInfo.placeHolderCmd ) kdDebug(32500) << kdBacktrace(); + undoRedoInfo.placeHolderCmd = new KMacroCommand( name ); + emit newCommand( undoRedoInfo.placeHolderCmd ); +} + +void KoTextObject::storeParagUndoRedoInfo( KoTextCursor * cursor, KoTextDocument::SelectionId selectionId ) +{ + undoRedoInfo.clear(); + undoRedoInfo.oldParagLayouts.clear(); + undoRedoInfo.text = " "; + undoRedoInfo.index = 1; + if ( cursor && !textdoc->hasSelection( selectionId, true ) ) { + KoTextParag * p = cursor->parag(); + undoRedoInfo.id = p->paragId(); + undoRedoInfo.eid = p->paragId(); + undoRedoInfo.oldParagLayouts << p->paragLayout(); + } + else{ + Q_ASSERT( textdoc->hasSelection( selectionId, true ) ); + KoTextParag *start = textdoc->selectionStart( selectionId ); + KoTextParag *end = textdoc->selectionEnd( selectionId ); + undoRedoInfo.id = start->paragId(); + undoRedoInfo.eid = end->paragId(); + for ( ; start && start != end->next() ; start = start->next() ) + { + undoRedoInfo.oldParagLayouts << start->paragLayout(); + //kdDebug(32500) << "KoTextObject:storeParagUndoRedoInfo storing counter " << start->paragLayout().counter.counterType << endl; + } + } +} + +void KoTextObject::doKeyboardAction( KoTextCursor * cursor, KoTextFormat * & /*currentFormat*/, KeyboardAction action ) +{ + KoTextParag * parag = cursor->parag(); + setLastFormattedParag( parag ); + emit hideCursor(); + bool doUpdateCurrentFormat = true; + switch ( action ) { + case ActionDelete: { + checkUndoRedoInfo( cursor, UndoRedoInfo::Delete ); + if ( !undoRedoInfo.valid() ) { + newPlaceHolderCommand( i18n("Delete Text") ); + undoRedoInfo.id = parag->paragId(); + undoRedoInfo.index = cursor->index(); + undoRedoInfo.text = QString::null; + undoRedoInfo.oldParagLayouts << parag->paragLayout(); + } + if ( !cursor->atParagEnd() ) + { + KoTextStringChar * ch = parag->at( cursor->index() ); + undoRedoInfo.text += ch->c; + copyCharFormatting( parag, cursor->index(), undoRedoInfo.text.length()-1, true ); + } + KoParagLayout paragLayout; + if ( parag->next() ) + paragLayout = parag->next()->paragLayout(); + + KoTextParag *old = cursor->parag(); + if ( cursor->remove() ) { + if ( old != cursor->parag() && m_lastFormatted == old ) // 'old' has been deleted + m_lastFormatted = cursor->parag() ? cursor->parag()->prev() : 0; + undoRedoInfo.text += "\n"; + undoRedoInfo.oldParagLayouts << paragLayout; + } else + emit paragraphModified( old, RemoveChar, cursor->index(), 1 ); + } break; + case ActionBackspace: { + // Remove counter + if ( parag->counter() && parag->counter()->style() != KoParagCounter::STYLE_NONE && cursor->index() == 0 ) { + // parag->decDepth(); // We don't have support for nested lists at the moment + // (only in titles, but you don't want Backspace to move it up) + KoParagCounter c; + c.setDepth( parag->counter()->depth() ); + KCommand *cmd=setCounterCommand( cursor, c ); + if(cmd) + emit newCommand(cmd); + } + else if ( !cursor->atParagStart() ) + { + checkUndoRedoInfo( cursor, UndoRedoInfo::Delete ); + if ( !undoRedoInfo.valid() ) { + newPlaceHolderCommand( i18n("Delete Text") ); + undoRedoInfo.id = parag->paragId(); + undoRedoInfo.index = cursor->index(); + undoRedoInfo.text = QString::null; + undoRedoInfo.oldParagLayouts << parag->paragLayout(); + } + undoRedoInfo.text.insert( 0, cursor->parag()->at( cursor->index()-1 ) ); + copyCharFormatting( cursor->parag(), cursor->index()-1, 0, true ); + undoRedoInfo.index = cursor->index()-1; + //KoParagLayout paragLayout = cursor->parag()->paragLayout(); + cursor->removePreviousChar(); + emit paragraphModified( cursor->parag(), RemoveChar, cursor->index(),1 ); + m_lastFormatted = cursor->parag(); + } else if ( parag->prev() ) { // joining paragraphs + emit paragraphDeleted( cursor->parag() ); + clearUndoRedoInfo(); + textdoc->setSelectionStart( KoTextDocument::Temp, cursor ); + cursor->gotoPreviousLetter(); + textdoc->setSelectionEnd( KoTextDocument::Temp, cursor ); + removeSelectedText( cursor, KoTextDocument::Temp, i18n( "Delete Text" ) ); + emit paragraphModified( cursor->parag(), AddChar, cursor->index(), cursor->parag()->length() - cursor->index() ); + } + } break; + case ActionReturn: { + checkUndoRedoInfo( cursor, UndoRedoInfo::Return ); + if ( !undoRedoInfo.valid() ) { + newPlaceHolderCommand( i18n("Insert Text") ); + undoRedoInfo.id = cursor->parag()->paragId(); + undoRedoInfo.index = cursor->index(); + undoRedoInfo.text = QString::null; + } + undoRedoInfo.text += "\n"; + if ( cursor->parag() ) + { + QString last_line = cursor->parag()->toString(); + last_line.remove(0,last_line.find(' ')+1); + + if( last_line.isEmpty() && cursor->parag()->counter() && cursor->parag()->counter()->numbering() == KoParagCounter::NUM_LIST ) //if the previous line the in paragraph is empty + { + KoParagCounter c; + KCommand *cmd=setCounterCommand( cursor, c ); + if(cmd) + emit newCommand(cmd); + setLastFormattedParag( cursor->parag() ); + cursor->parag()->setNoCounter(); + + formatMore( 2 ); + emit repaintChanged( this ); + emit ensureCursorVisible(); + emit showCursor(); + emit updateUI( doUpdateCurrentFormat ); + return; + } + else + cursor->splitAndInsertEmptyParag(); + } + + Q_ASSERT( cursor->parag()->prev() ); + setLastFormattedParag( cursor->parag() ); + + doUpdateCurrentFormat = false; + KoParagStyle * style = cursor->parag()->prev()->style(); + if ( style ) + { + KoParagStyle * newStyle = style->followingStyle(); + if ( newStyle && style != newStyle ) // different "following style" applied + { + doUpdateCurrentFormat = true; + //currentFormat = textdoc->formatCollection()->format( cursor->parag()->paragFormat() ); + //kdDebug(32500) << "KoTextFrameSet::doKeyboardAction currentFormat=" << currentFormat << " " << currentFormat->key() << endl; + } + } + if ( cursor->parag()->joinBorder() && cursor->parag()->bottomBorder().width() > 0 ) + cursor->parag()->prev()->setChanged( true ); + if ( cursor->parag()->joinBorder() && cursor->parag()->next() && cursor->parag()->next()->joinBorder() && cursor->parag()->bottomBorder() == cursor->parag()->next()->bottomBorder()) + cursor->parag()->next()->setChanged( true ); + emit paragraphCreated( cursor->parag() ); + + } break; + case ActionKill: + // Nothing to kill if at end of very last paragraph + if ( !cursor->atParagEnd() || cursor->parag()->next() ) { + checkUndoRedoInfo( cursor, UndoRedoInfo::Delete ); + if ( !undoRedoInfo.valid() ) { + newPlaceHolderCommand( i18n("Delete Text") ); + undoRedoInfo.id = cursor->parag()->paragId(); + undoRedoInfo.index = cursor->index(); + undoRedoInfo.text = QString::null; + undoRedoInfo.oldParagLayouts << parag->paragLayout(); + } + if ( cursor->atParagEnd() ) { + // Get paraglayout from next parag (next can't be 0 here) + KoParagLayout paragLayout = parag->next()->paragLayout(); + if ( cursor->remove() ) + { + m_lastFormatted = cursor->parag(); + undoRedoInfo.text += "\n"; + undoRedoInfo.oldParagLayouts << paragLayout; + } + } else { + int oldLen = undoRedoInfo.text.length(); + undoRedoInfo.text += cursor->parag()->string()->toString().mid( cursor->index() ); + for ( int i = cursor->index(); i < cursor->parag()->length(); ++i ) + copyCharFormatting( cursor->parag(), i, oldLen + i - cursor->index(), true ); + cursor->killLine(); + emit paragraphModified( cursor->parag(), RemoveChar, cursor->index(), cursor->parag()->length()-cursor->index() ); + } + } + break; + } + + if ( !undoRedoInfo.customItemsMap.isEmpty() ) + clearUndoRedoInfo(); + + formatMore( 2 ); + emit repaintChanged( this ); + emit ensureCursorVisible(); + emit showCursor(); + emit updateUI( doUpdateCurrentFormat ); +} + +void KoTextObject::insert( KoTextCursor * cursor, KoTextFormat * currentFormat, + const QString &txt, const QString & commandName, KoTextDocument::SelectionId selectionId, + int insertFlags, CustomItemsMap customItemsMap ) +{ + if ( protectContent() ) + return; + const bool checkNewLine = insertFlags & CheckNewLine; + const bool removeSelected = ( insertFlags & DoNotRemoveSelected ) == 0; + const bool repaint = ( insertFlags & DoNotRepaint ) == 0; + //kdDebug(32500) << "KoTextObject::insert txt=" << txt << endl; + bool tinyRepaint = !checkNewLine; + if ( repaint ) + emit hideCursor(); + if ( textdoc->hasSelection( selectionId, true ) && removeSelected ) { + kdDebug() << k_funcinfo << "removing selection " << selectionId << endl; + // call replaceSelectionCommand, which will call insert() back, but this time without a selection. + emitNewCommand(replaceSelectionCommand( cursor, txt, commandName, selectionId, insertFlags, customItemsMap )); + return; + } + // Now implement overwrite mode, similarly. + if ( insertFlags & OverwriteMode ) { + textdoc->setSelectionStart( KoTextDocument::Temp, cursor ); + KoTextCursor oc = *cursor; + kdDebug(32500) << "overwrite: going to insert " << txt.length() << " chars; idx=" << oc.index() << endl; + oc.setIndex( QMIN( oc.index() + (int)txt.length(), oc.parag()->lastCharPos() + 1 ) ); + kdDebug(32500) << "overwrite: removing from " << cursor->index() << " to " << oc.index() << endl; + if ( oc.index() > cursor->index() ) + { + textdoc->setSelectionEnd( KoTextDocument::Temp, &oc ); + int newInsertFlags = insertFlags & ~OverwriteMode; + newInsertFlags &= ~DoNotRemoveSelected; + emitNewCommand(replaceSelectionCommand( cursor, txt, commandName, KoTextDocument::Temp, newInsertFlags, customItemsMap )); + return; + } + } + KoTextCursor c2 = *cursor; + // Make everything ready for undo/redo (new command, or add to current one) + if ( !customItemsMap.isEmpty() ) + clearUndoRedoInfo(); + checkUndoRedoInfo( cursor, UndoRedoInfo::Insert ); + if ( !undoRedoInfo.valid() ) { + if ( !commandName.isNull() ) // see replace-selection + newPlaceHolderCommand( commandName ); + undoRedoInfo.id = cursor->parag()->paragId(); + undoRedoInfo.index = cursor->index(); + undoRedoInfo.text = QString::null; + } + int oldLen = undoRedoInfo.text.length(); + KoTextCursor oldCursor = *cursor; + bool wasChanged = cursor->parag()->hasChanged(); + int origLine; // the line the cursor was on before the insert + oldCursor.parag()->lineStartOfChar( oldCursor.index(), 0, &origLine ); + + // insert the text - finally! + cursor->insert( txt, checkNewLine ); + + setLastFormattedParag( checkNewLine ? oldCursor.parag() : cursor->parag() ); + + if ( !customItemsMap.isEmpty() ) { + customItemsMap.insertItems( oldCursor, txt.length() ); + undoRedoInfo.customItemsMap = customItemsMap; + tinyRepaint = false; + } + + textdoc->setSelectionStart( KoTextDocument::Temp, &oldCursor ); + textdoc->setSelectionEnd( KoTextDocument::Temp, cursor ); + //kdDebug(32500) << "KoTextObject::insert setting format " << currentFormat << endl; + textdoc->setFormat( KoTextDocument::Temp, currentFormat, KoTextFormat::Format ); + textdoc->setFormat( KoTextDocument::InputMethodPreedit, currentFormat, KoTextFormat::Format ); + textdoc->removeSelection( KoTextDocument::Temp ); + + if ( !customItemsMap.isEmpty() ) { + // Some custom items (e.g. variables) depend on the format + CustomItemsMap::Iterator it = customItemsMap.begin(); + for ( ; it != customItemsMap.end(); ++it ) + it.data()->resize(); + } + + // Speed optimization: if we only type a char, and it doesn't + // invalidate the next parag, only format the current one + // ### This idea is wrong. E.g. when the last parag grows and must create a new page. +#if 0 + KoTextParag *parag = cursor->parag(); + if ( !checkNewLine && m_lastFormatted == parag && ( !parag->next() || parag->next()->isValid() ) ) + { + parag->format(); + m_lastFormatted = m_lastFormatted->next(); + } +#endif + // Call formatMore until necessary. This will create new pages if needed. + // Doing this here means callers don't have to do it, and cursor can be positionned correctly. + ensureFormatted( cursor->parag() ); + + // Speed optimization: if we only type a char, only repaint from current line + // (In fact the one before. When typing a space, a word could move up to the + // line before, we need to repaint it too...) + if ( !checkNewLine && tinyRepaint && !wasChanged ) + { + // insert() called format() which called setChanged(). + // We're reverting that, and calling setLineChanged() only. + Q_ASSERT( cursor->parag() == oldCursor.parag() ); // no newline! + KoTextParag* parag = cursor->parag(); + // If the new char led to a new line, + // the wordwrap could have changed on the line above + // This is why we use origLine and not calling lineStartOfChar here. + parag->setChanged( false ); + parag->setLineChanged( origLine - 1 ); // if origLine=0, it'll pass -1, which is 'all' + } + + if ( repaint ) { + emit repaintChanged( this ); + emit ensureCursorVisible(); + emit showCursor(); + // we typed the first char of a paragraph in AlignAuto mode -> show correct alignment in UI + if ( oldCursor.index() == 0 && oldCursor.parag()->alignment() == Qt::AlignAuto ) + emit updateUI( true ); + + } + undoRedoInfo.text += txt; + for ( int i = 0; i < (int)txt.length(); ++i ) { + if ( txt[ oldLen + i ] != '\n' ) + copyCharFormatting( c2.parag(), c2.index(), oldLen + i, false ); + c2.gotoNextLetter(); + } + + if ( !removeSelected ) { + // ## not sure why we do this. I'd prefer leaving the selection unchanged... + // but then it'd need adjustements in the offsets etc. + if ( textdoc->removeSelection( selectionId ) && repaint ) + selectionChangedNotify(); // does the repaint + } + if ( !customItemsMap.isEmpty() + && !commandName.isNull() /* see replace-selection; #139890 */ ) { + clearUndoRedoInfo(); + } + + // Notifications + emit paragraphModified( oldCursor.parag(), AddChar, cursor->index(), txt.length() ); + if (checkNewLine) { + KoTextParag* p = oldCursor.parag()->next(); + while ( p && p != cursor->parag() ) { + emit paragraphCreated( p ); + p = p->next(); + } + } +} + +void KoTextObject::pasteText( KoTextCursor * cursor, const QString & text, KoTextFormat * currentFormat, bool removeSelected ) +{ + if ( protectContent() ) + return; + kdDebug(32500) << "KoTextObject::pasteText cursor parag=" << cursor->parag()->paragId() << endl; + QString t = text; + // Need to convert CRLF to NL + QRegExp crlf( QString::fromLatin1("\r\n") ); + t.replace( crlf, QChar('\n') ); + // Convert non-printable chars + for ( int i=0; (uint) i<t.length(); i++ ) { + if ( t[ i ] < ' ' && t[ i ] != '\n' && t[ i ] != '\t' ) + t[ i ] = ' '; + } + if ( !t.isEmpty() ) + { + int insertFlags = CheckNewLine; + if ( !removeSelected ) + insertFlags |= DoNotRemoveSelected; + insert( cursor, currentFormat, t, i18n("Paste Text"), + KoTextDocument::Standard, insertFlags ); + formatMore( 2 ); + emit repaintChanged( this ); + } +} + +KCommand* KoTextObject::setParagLayoutCommand( KoTextCursor * cursor, const KoParagLayout& paragLayout, + KoTextDocument::SelectionId selectionId, int paragLayoutFlags, + int marginIndex, bool createUndoRedo ) +{ + if ( protectContent() ) + return 0; + storeParagUndoRedoInfo( cursor, selectionId ); + undoRedoInfo.type = UndoRedoInfo::Invalid; // tricky, we don't want clear() to create a command + if ( paragLayoutFlags != 0 ) + { + emit hideCursor(); + if ( !textdoc->hasSelection( selectionId, true ) ) { + cursor->parag()->setParagLayout( paragLayout, paragLayoutFlags, marginIndex ); + setLastFormattedParag( cursor->parag() ); + } else { + KoTextParag *start = textdoc->selectionStart( selectionId ); + KoTextParag *end = textdoc->selectionEnd( selectionId ); + for ( ; start && start != end->next() ; start = start->next() ) { + if ( paragLayoutFlags == KoParagLayout::BulletNumber && start->length() <= 1 ) + continue; // don't apply to empty paragraphs (#25742, #34062) + start->setParagLayout( paragLayout, paragLayoutFlags, marginIndex ); + } + setLastFormattedParag( start ); + } + + formatMore( 2 ); + emit repaintChanged( this ); + emit showCursor(); + emit updateUI( true ); + + if ( createUndoRedo ) + { + //kdDebug(32500) << "KoTextObject::applyStyle KoTextParagCommand" << endl; + KoTextDocCommand * cmd = new KoTextParagCommand( textdoc, undoRedoInfo.id, undoRedoInfo.eid, + undoRedoInfo.oldParagLayouts, + paragLayout, paragLayoutFlags, + (QStyleSheetItem::Margin)marginIndex ); + textdoc->addCommand( cmd ); + return new KoTextCommand( this, /*cmd, */"related to KoTextParagCommand" ); + } + } + return 0; +} + + +void KoTextObject::applyStyle( KoTextCursor * cursor, const KoParagStyle * newStyle, + KoTextDocument::SelectionId selectionId, + int paragLayoutFlags, int formatFlags, + bool createUndoRedo, bool interactive ) +{ + KCommand *cmd = applyStyleCommand( cursor, newStyle, selectionId, + paragLayoutFlags, formatFlags, + createUndoRedo, interactive ); + if ( createUndoRedo && cmd ) + emit newCommand( cmd ); + else + Q_ASSERT( !cmd ); // mem leak, if applyStyleCommand created a command despite createUndoRedo==false! +} + +KCommand *KoTextObject::applyStyleCommand( KoTextCursor * cursor, const KoParagStyle * newStyle, + KoTextDocument::SelectionId selectionId, + int paragLayoutFlags, int formatFlags, + bool createUndoRedo, bool interactive ) +{ + if ( protectContent()) + return 0L; + if ( interactive ) + emit hideCursor(); + if ( !textdoc->hasSelection( selectionId, true ) && !cursor) + return 0L; + /// Applying a style is three distinct operations : + /// 1 - Changing the paragraph settings (setParagLayout) + /// 2 - Changing the character formatting for each char in the paragraph (setFormat(indices)) + /// 3 - Changing the character formatting for the whole paragraph (setFormat()) [just in case] + /// -> We need a macro command to hold the 3 commands + KMacroCommand * macroCmd = createUndoRedo ? new KMacroCommand( i18n("Apply Style %1"). + arg(newStyle->displayName() ) ) : 0; + + // 1 + //kdDebug(32500) << "KoTextObject::applyStyle setParagLayout" << endl; + KCommand* cmd = setParagLayoutCommand( cursor, newStyle->paragLayout(), selectionId, paragLayoutFlags, -1, createUndoRedo ); + if ( cmd ) + macroCmd->addCommand( cmd ); + + // 2 + //kdDebug(32500) << "KoTextObject::applyStyle gathering text and formatting" << endl; + KoTextParag * firstParag; + KoTextParag * lastParag; + if ( !textdoc->hasSelection( selectionId, true ) ) { + // No selection -> apply style formatting to the whole paragraph + firstParag = cursor->parag(); + lastParag = cursor->parag(); + } + else + { + firstParag = textdoc->selectionStart( selectionId ); + lastParag = textdoc->selectionEnd( selectionId ); + } + + if ( formatFlags != 0 ) + { + KoTextFormat * newFormat = textdoc->formatCollection()->format( &newStyle->format() ); + + if ( createUndoRedo ) + { + QValueList<KoTextFormat *> lstFormats; + //QString str; + for ( KoTextParag * parag = firstParag ; parag && parag != lastParag->next() ; parag = parag->next() ) + { + //str += parag->string()->toString() + '\n'; + lstFormats.append( parag->paragFormat() ); + } + KoTextCursor c1( textdoc ); + c1.setParag( firstParag ); + c1.setIndex( 0 ); + KoTextCursor c2( textdoc ); + c2.setParag( lastParag ); + c2.setIndex( lastParag->string()->length() ); + undoRedoInfo.clear(); + undoRedoInfo.type = UndoRedoInfo::Invalid; // same trick + readFormats( c1, c2 ); // gather char-format info but not paraglayouts nor customitems + + KoTextDocCommand * cmd = new KoTextFormatCommand( textdoc, firstParag->paragId(), 0, + lastParag->paragId(), c2.index(), + undoRedoInfo.text.rawData(), newFormat, + formatFlags ); + textdoc->addCommand( cmd ); + macroCmd->addCommand( new KoTextCommand( this, /*cmd, */"related to KoTextFormatCommand" ) ); + + // sub-command for '3' (paragFormat) + cmd = new KoParagFormatCommand( textdoc, firstParag->paragId(), lastParag->paragId(), + lstFormats, newFormat ); + textdoc->addCommand( cmd ); + macroCmd->addCommand( new KoTextCommand( this, /*cmd, */"related to KoParagFormatCommand" ) ); + } + + // apply '2' and '3' (format) + for ( KoTextParag * parag = firstParag ; parag && parag != lastParag->next() ; parag = parag->next() ) + { + //kdDebug(32500) << "KoTextObject::applyStyle parag:" << parag->paragId() + // << ", from 0 to " << parag->string()->length() << ", format=" << newFormat << endl; + parag->setFormat( 0, parag->string()->length(), newFormat, true, formatFlags ); + parag->setFormat( newFormat ); + } + //currentFormat = textdoc->formatCollection()->format( newFormat ); + //kdDebug(32500) << "KoTextObject::applyStyle currentFormat=" << currentFormat << " " << currentFormat->key() << endl; + } + + //resize all variables after applying the style + QPtrListIterator<KoTextCustomItem> cit( textdoc->allCustomItems() ); + for ( ; cit.current() ; ++cit ) + cit.current()->resize(); + + + if ( interactive ) + { + setLastFormattedParag( firstParag ); + formatMore( 2 ); + emit repaintChanged( this ); + emit updateUI( true ); + emit showCursor(); + } + + undoRedoInfo.clear(); + + return macroCmd; +} + +void KoTextObject::applyStyleChange( KoStyleChangeDefMap changed ) +{ +#if 0 //#ifndef NDEBUG + kdDebug(32500) << "KoTextObject::applyStyleChange " << changed.count() << " styles." << endl; + for( KoStyleChangeDefMap::const_iterator it = changed.begin(); it != changed.end(); ++it ) { + kdDebug(32500) << " " << it.key()->name() + << " paragLayoutChanged=" << (*it).paragLayoutChanged + << " formatChanged=" << (*it).formatChanged + << endl; + } +#endif + + KoTextParag *p = textdoc->firstParag(); + while ( p ) { + KoStyleChangeDefMap::Iterator it = changed.find( p->style() ); + if ( it != changed.end() ) + { + if ( (*it).paragLayoutChanged == -1 || (*it).formatChanged == -1 ) // Style has been deleted + { + p->setStyle( m_defaultStyle ); // keeps current formatting + // TODO, make this undoable somehow + } + else + { + // Apply this style again, to get the changes + KoTextCursor cursor( textdoc ); + cursor.setParag( p ); + cursor.setIndex( 0 ); + //kdDebug(32500) << "KoTextObject::applyStyleChange applying to paragraph " << p << " " << p->paragId() << endl; + applyStyle( &cursor, it.key(), + KoTextDocument::Temp, // A selection we can't have at this point + (*it).paragLayoutChanged, (*it).formatChanged, + false, false ); // don't create undo/redo, not interactive + } + } else { + //kdDebug(32500) << "KoTextObject::applyStyleChange leaving paragraph unchanged: " << p << " " << p->paragId() << endl; + } + + p = p->next(); + } + setLastFormattedParag( textdoc->firstParag() ); + formatMore( 2 ); + emit repaintChanged( this ); + emit updateUI( true ); +} + +/** Implementation of setFormatCommand from KoTextFormatInterface - apply change to the whole document */ +KCommand *KoTextObject::setFormatCommand( const KoTextFormat *format, int flags, bool zoomFont ) +{ + textdoc->selectAll( KoTextDocument::Temp ); + KCommand *cmd = setFormatCommand( 0L, 0L, format, flags, zoomFont, KoTextDocument::Temp ); + textdoc->removeSelection( KoTextDocument::Temp ); + return cmd; +} + +KCommand * KoTextObject::setFormatCommand( KoTextCursor * cursor, KoTextFormat ** pCurrentFormat, const KoTextFormat *format, int flags, bool /*zoomFont*/, KoTextDocument::SelectionId selectionId ) +{ + KCommand *ret = 0; + if ( protectContent() ) + return ret; + + KoTextFormat* newFormat = 0; + // Get new format from collection if + // - caller has notion of a "current format" and new format is different + // - caller has no notion of "current format", e.g. whole textobjects + bool isNewFormat = ( pCurrentFormat && *pCurrentFormat && (*pCurrentFormat)->key() != format->key() ); + if ( isNewFormat || !pCurrentFormat ) + { +#if 0 + int origFontSize = 0; + if ( zoomFont ) // The format has a user-specified font (e.g. setting a style, or a new font size) + { + origFontSize = format->pointSize(); + format->setPointSize( zoomedFontSize( origFontSize ) ); + //kdDebug(32500) << "KoTextObject::setFormatCommand format " << format->key() << " zoomed from " << origFontSize << " to " << format->font().pointSizeFloat() << endl; + } +#endif + // Remove ref to current format, if caller wanted that + if ( pCurrentFormat ) + (*pCurrentFormat)->removeRef(); + // Find format in collection + newFormat = textdoc->formatCollection()->format( format ); + if ( newFormat->isMisspelled() ) { + KoTextFormat fNoMisspelled( *newFormat ); + newFormat->removeRef(); + fNoMisspelled.setMisspelled( false ); + newFormat = textdoc->formatCollection()->format( &fNoMisspelled ); + } + if ( pCurrentFormat ) + (*pCurrentFormat) = newFormat; + } + + if ( textdoc->hasSelection( selectionId, true ) ) { + emit hideCursor(); + KoTextCursor c1 = textdoc->selectionStartCursor( selectionId ); + KoTextCursor c2 = textdoc->selectionEndCursor( selectionId ); + undoRedoInfo.clear(); + int id = c1.parag()->paragId(); + int index = c1.index(); + int eid = c2.parag()->paragId(); + int eindex = c2.index(); + readFormats( c1, c2 ); // read previous formatting info + //kdDebug(32500) << "KoTextObject::setFormatCommand undoredo info done" << endl; + textdoc->setFormat( selectionId, format, flags ); + if ( !undoRedoInfo.customItemsMap.isEmpty() ) + { + // Some custom items (e.g. variables) depend on the format + CustomItemsMap::Iterator it = undoRedoInfo.customItemsMap.begin(); + for ( ; it != undoRedoInfo.customItemsMap.end(); ++it ) + it.data()->resize(); + } + KoTextFormatCommand *cmd = new KoTextFormatCommand( + textdoc, id, index, eid, eindex, undoRedoInfo.text.rawData(), + format, flags ); + textdoc->addCommand( cmd ); + ret = new KoTextCommand( this, /*cmd, */i18n("Format Text") ); + undoRedoInfo.clear(); + setLastFormattedParag( c1.parag() ); + formatMore( 2 ); + emit repaintChanged( this ); + emit showCursor(); + } + if ( isNewFormat ) { + emit showCurrentFormat(); + //kdDebug(32500) << "KoTextObject::setFormatCommand index=" << cursor->index() << " length-1=" << cursor->parag()->length() - 1 << endl; + if ( cursor && cursor->index() == cursor->parag()->length() - 1 ) { + newFormat->addRef(); + cursor->parag()->string()->setFormat( cursor->index(), newFormat, TRUE ); + if ( cursor->parag()->length() == 1 ) { + newFormat->addRef(); + cursor->parag()->setFormat( newFormat ); + cursor->parag()->invalidate(0); + cursor->parag()->format(); + emit repaintChanged( this ); + } + } + } + return ret; +} + +void KoTextObject::setFormat( KoTextCursor * cursor, KoTextFormat ** currentFormat, KoTextFormat *format, int flags, bool zoomFont ) +{ + if ( protectContent() ) + return; + KCommand *cmd = setFormatCommand( cursor, currentFormat, format, flags, zoomFont ); + if (cmd) + emit newCommand( cmd ); +} + +void KoTextObject::emitNewCommand(KCommand *cmd) +{ + if(cmd) + emit newCommand( cmd ); +} + +KCommand *KoTextObject::setCounterCommand( KoTextCursor * cursor, const KoParagCounter & counter, KoTextDocument::SelectionId selectionId ) +{ + if ( protectContent() ) + return 0L; + const KoParagCounter * curCounter = 0L; + if(cursor) + curCounter=cursor->parag()->counter(); + if ( !textdoc->hasSelection( selectionId, true ) && + curCounter && counter == *curCounter ) { + return 0L; + } + emit hideCursor(); + storeParagUndoRedoInfo( cursor, selectionId ); + if ( !textdoc->hasSelection( selectionId, true ) && cursor) { + cursor->parag()->setCounter( counter ); + setLastFormattedParag( cursor->parag() ); + } else { + KoTextParag *start = textdoc->selectionStart( selectionId ); + KoTextParag *end = textdoc->selectionEnd( selectionId ); +#if 0 + // Special hack for BR25742, don't apply bullet to last empty parag of the selection + if ( start != end && end->length() <= 1 ) + { + end = end->prev(); + undoRedoInfo.eid = end->paragId(); + } +#endif + setLastFormattedParag( start ); + for ( ; start && start != end->next() ; start = start->next() ) + { + if ( start->length() > 1 ) // don't apply to empty paragraphs (#25742, #34062) + start->setCounter( counter ); + } + } + formatMore( 2 ); + emit repaintChanged( this ); + if ( !undoRedoInfo.newParagLayout.counter ) + undoRedoInfo.newParagLayout.counter = new KoParagCounter; + *undoRedoInfo.newParagLayout.counter = counter; + KoTextParagCommand *cmd = new KoTextParagCommand( + textdoc, undoRedoInfo.id, undoRedoInfo.eid, + undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout, + KoParagLayout::BulletNumber ); + textdoc->addCommand( cmd ); + + undoRedoInfo.clear(); // type is still Invalid -> no command created + emit showCursor(); + emit updateUI( true ); + return new KoTextCommand( this, /*cmd, */i18n("Change List Type") ); +} + +KCommand * KoTextObject::setAlignCommand( KoTextCursor * cursor, int align, KoTextDocument::SelectionId selectionId ) +{ + if ( protectContent() ) + return 0L; + if ( !textdoc->hasSelection( selectionId, true ) && cursor && + (int)cursor->parag()->alignment() == align ) + return 0L; // No change needed. + + emit hideCursor(); + storeParagUndoRedoInfo( cursor ,selectionId ); + if ( !textdoc->hasSelection( selectionId, true ) &&cursor ) { + cursor->parag()->setAlign(align); + setLastFormattedParag( cursor->parag() ); + } + else + { + KoTextParag *start = textdoc->selectionStart( selectionId ); + KoTextParag *end = textdoc->selectionEnd( selectionId ); + setLastFormattedParag( start ); + for ( ; start && start != end->next() ; start = start->next() ) + start->setAlign(align); + } + formatMore( 2 ); + emit repaintChanged( this ); + undoRedoInfo.newParagLayout.alignment = align; + KoTextParagCommand *cmd = new KoTextParagCommand( + textdoc, undoRedoInfo.id, undoRedoInfo.eid, + undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout, + KoParagLayout::Alignment ); + textdoc->addCommand( cmd ); + undoRedoInfo.clear(); // type is still Invalid -> no command created + emit showCursor(); + emit updateUI( true ); + return new KoTextCommand( this, /*cmd, */i18n("Change Alignment") ); +} + +KCommand * KoTextObject::setMarginCommand( KoTextCursor * cursor, QStyleSheetItem::Margin m, double margin , KoTextDocument::SelectionId selectionId ) { + if ( protectContent() ) + return 0L; + + //kdDebug(32500) << "KoTextObject::setMargin " << m << " to value " << margin << endl; + //kdDebug(32500) << "Current margin is " << cursor->parag()->margin(m) << endl; + if ( !textdoc->hasSelection( selectionId, true ) && cursor && + cursor->parag()->margin(m) == margin ) + return 0L; // No change needed. + + emit hideCursor(); + storeParagUndoRedoInfo( cursor, selectionId ); + if ( !textdoc->hasSelection( selectionId, true )&&cursor ) { + cursor->parag()->setMargin(m, margin); + setLastFormattedParag( cursor->parag() ); + } + else + { + KoTextParag *start = textdoc->selectionStart( selectionId ); + KoTextParag *end = textdoc->selectionEnd( selectionId ); + setLastFormattedParag( start ); + for ( ; start && start != end->next() ; start = start->next() ) + start->setMargin(m, margin); + } + formatMore( 2 ); + emit repaintChanged( this ); + undoRedoInfo.newParagLayout.margins[m] = margin; + KoTextParagCommand *cmd = new KoTextParagCommand( + textdoc, undoRedoInfo.id, undoRedoInfo.eid, + undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout, + KoParagLayout::Margins, m ); + textdoc->addCommand( cmd ); + QString name; + if ( m == QStyleSheetItem::MarginFirstLine ) + name = i18n("Change First Line Indent"); + else if ( m == QStyleSheetItem::MarginLeft || m == QStyleSheetItem::MarginRight ) + name = i18n("Change Indent"); + else + name = i18n("Change Paragraph Spacing"); + undoRedoInfo.clear(); + emit showCursor(); + emit updateUI( true ); + return new KoTextCommand( this, /*cmd, */name ); +} + +KCommand * KoTextObject::setBackgroundColorCommand( KoTextCursor * cursor, + const QColor & color, + KoTextDocument::SelectionId selectionId ) { + if ( protectContent() ) + return 0L; + + if ( !textdoc->hasSelection( selectionId, true ) && cursor && + cursor->parag()->backgroundColor() == color ) + return 0L; // No change needed. + + emit hideCursor(); + storeParagUndoRedoInfo( cursor, selectionId ); + if ( !textdoc->hasSelection( selectionId, true )&&cursor ) + { + // Update a single paragraph + cursor->parag()->setBackgroundColor(color); + setLastFormattedParag( cursor->parag() ); + } + else + { + // Update multiple paragraphs + KoTextParag *start = textdoc->selectionStart( selectionId ); + KoTextParag *end = textdoc->selectionEnd( selectionId ); + setLastFormattedParag( start ); + for ( ; start && start != end->next() ; start = start->next() ) + start->setBackgroundColor(color); + } + formatMore( 2 ); + emit repaintChanged( this ); + + // Update undo/redo info + undoRedoInfo.newParagLayout.backgroundColor = color; + KoTextParagCommand *cmd = new KoTextParagCommand( + textdoc, undoRedoInfo.id, undoRedoInfo.eid, + undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout, + KoParagLayout::BackgroundColor ); + textdoc->addCommand( cmd ); + undoRedoInfo.clear(); + + emit showCursor(); + emit updateUI( true ); + return new KoTextCommand( this, /*cmd, */ i18n("Change Paragraph Background Color" ) ); +} + +KCommand * KoTextObject::setLineSpacingCommand( KoTextCursor * cursor, double spacing, KoParagLayout::SpacingType _type, KoTextDocument::SelectionId selectionId ) +{ + if ( protectContent() ) + return 0L; + //kdDebug(32500) << "KoTextObject::setLineSpacing to value " << spacing << endl; + //kdDebug(32500) << "Current spacing is " << cursor->parag()->kwLineSpacing() << endl; + //kdDebug(32500) << "Comparison says " << ( cursor->parag()->kwLineSpacing() == spacing ) << endl; + //kdDebug(32500) << "hasSelection " << textdoc->hasSelection( selectionId ) << endl; + if ( !textdoc->hasSelection( selectionId, true ) && cursor && + cursor->parag()->kwLineSpacing() == spacing + && cursor->parag()->kwLineSpacingType() == _type) + return 0L; // No change needed. + + emit hideCursor(); + storeParagUndoRedoInfo( cursor, selectionId ); + if ( !textdoc->hasSelection( selectionId, true ) && cursor ) { + cursor->parag()->setLineSpacing(spacing); + cursor->parag()->setLineSpacingType( _type); + setLastFormattedParag( cursor->parag() ); + } + else + { + KoTextParag *start = textdoc->selectionStart( selectionId ); + KoTextParag *end = textdoc->selectionEnd( selectionId ); + setLastFormattedParag( start ); + for ( ; start && start != end->next() ; start = start->next() ) + { + start->setLineSpacing(spacing); + start->setLineSpacingType( _type); + } + } + formatMore( 2 ); + emit repaintChanged( this ); + undoRedoInfo.newParagLayout.setLineSpacingValue( spacing ); + undoRedoInfo.newParagLayout.lineSpacingType = _type; + KoTextParagCommand *cmd = new KoTextParagCommand( + textdoc, undoRedoInfo.id, undoRedoInfo.eid, + undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout, + KoParagLayout::LineSpacing ); + textdoc->addCommand( cmd ); + + undoRedoInfo.clear(); + emit showCursor(); + return new KoTextCommand( this, /*cmd, */i18n("Change Line Spacing") ); +} + + +KCommand * KoTextObject::setBordersCommand( KoTextCursor * cursor, const KoBorder& leftBorder, const KoBorder& rightBorder, const KoBorder& topBorder, const KoBorder& bottomBorder , KoTextDocument::SelectionId selectionId ) +{ + if ( protectContent() ) + return 0L; + if ( !textdoc->hasSelection( selectionId, true ) && cursor && + cursor->parag()->leftBorder() ==leftBorder && + cursor->parag()->rightBorder() ==rightBorder && + cursor->parag()->topBorder() ==topBorder && + cursor->parag()->bottomBorder() ==bottomBorder ) + return 0L; // No change needed. + + emit hideCursor(); + storeParagUndoRedoInfo( cursor, selectionId ); + if ( !textdoc->hasSelection( selectionId, true ) ) { + cursor->parag()->setLeftBorder(leftBorder); + cursor->parag()->setRightBorder(rightBorder); + cursor->parag()->setBottomBorder(bottomBorder); + cursor->parag()->setTopBorder(topBorder); + setLastFormattedParag( cursor->parag() ); + + if ( cursor->parag()->next() ) + cursor->parag()->next()->setChanged( true ); + if ( cursor->parag()->prev() ) + cursor->parag()->prev()->setChanged( true ); + } + else + { + KoTextParag *start = textdoc->selectionStart( selectionId ); + KoTextParag *end = textdoc->selectionEnd( selectionId ); + setLastFormattedParag( start ); + for ( ; start && start != end->next() ; start = start->next() ) + { + start->setLeftBorder(leftBorder); + start->setRightBorder(rightBorder); + start->setTopBorder(topBorder); + start->setBottomBorder(bottomBorder); + } + textdoc->selectionStart( selectionId )->setTopBorder(topBorder); + + if ( start && start->prev() ) + start->prev()->setChanged( true ); + if ( end && end->next() ) + end->next()->setChanged( true ); + } + formatMore( 2 ); + emit repaintChanged( this ); + undoRedoInfo.newParagLayout.leftBorder=leftBorder; + undoRedoInfo.newParagLayout.rightBorder=rightBorder; + undoRedoInfo.newParagLayout.topBorder=topBorder; + undoRedoInfo.newParagLayout.bottomBorder=bottomBorder; + + KoTextParagCommand *cmd = new KoTextParagCommand( + textdoc, undoRedoInfo.id, undoRedoInfo.eid, + undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout, + KoParagLayout::Borders, (QStyleSheetItem::Margin)-1 ); + textdoc->addCommand( cmd ); + + undoRedoInfo.clear(); + emit showCursor(); + emit updateUI( true ); + return new KoTextCommand( this, /*cmd, */i18n("Change Borders") ); +} + +KCommand * KoTextObject::setJoinBordersCommand( KoTextCursor * cursor, bool join, KoTextDocument::SelectionId selectionId ) +{ + if ( protectContent() ) + return 0L; + if ( !textdoc->hasSelection( selectionId, true ) && + cursor && cursor->parag()->joinBorder() == join ) + return 0L; // No change needed. + + emit hideCursor(); + storeParagUndoRedoInfo( cursor, KoTextDocument::Standard ); + if ( !textdoc->hasSelection( selectionId, true ) ) + { + cursor->parag()->setJoinBorder( join ); + setLastFormattedParag( cursor->parag() ); + + if ( cursor->parag()->next() ) + cursor->parag()->next()->setChanged( true ); + if ( cursor->parag()->prev() ) + cursor->parag()->prev()->setChanged( true ); + } + else + { + KoTextParag *start = textdoc->selectionStart( selectionId ); + KoTextParag *end = textdoc->selectionEnd( selectionId ); + setLastFormattedParag( start ); + for ( ; start && start != end->next() ; start = start->next() ) + { + start->setJoinBorder( true ); + } + end->setJoinBorder ( true ); + + if ( start && start->prev() ) + start->prev()->setChanged( true ); + if ( end && end->next() ) + end->next()->setChanged( true ); + } + formatMore( 2 ); + + emit repaintChanged( this ); + undoRedoInfo.newParagLayout.joinBorder=join; + + KoTextParagCommand *cmd = new KoTextParagCommand( + textdoc, undoRedoInfo.id, undoRedoInfo.eid, + undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout, + KoParagLayout::Borders, (QStyleSheetItem::Margin)-1 ); + textdoc->addCommand( cmd ); + + undoRedoInfo.clear(); + emit ensureCursorVisible(); + emit showCursor(); + emit updateUI( true ); + return new KoTextCommand( this, /*cmd, */i18n("Change Join Borders") ); +} + + +KCommand * KoTextObject::setTabListCommand( KoTextCursor * cursor, const KoTabulatorList &tabList, KoTextDocument::SelectionId selectionId ) +{ + if ( protectContent() ) + return 0L; + if ( !textdoc->hasSelection( selectionId, true ) && cursor && + cursor->parag()->tabList() == tabList ) + return 0L; // No change needed. + + emit hideCursor(); + storeParagUndoRedoInfo( cursor, selectionId ); + + if ( !textdoc->hasSelection( selectionId, true ) && cursor ) { + cursor->parag()->setTabList( tabList ); + setLastFormattedParag( cursor->parag() ); + } + else + { + KoTextParag *start = textdoc->selectionStart( selectionId ); + KoTextParag *end = textdoc->selectionEnd( selectionId ); + setLastFormattedParag( start ); + for ( ; start && start != end->next() ; start = start->next() ) + start->setTabList( tabList ); + } + + formatMore( 2 ); + emit repaintChanged( this ); + undoRedoInfo.newParagLayout.setTabList( tabList ); + KoTextParagCommand *cmd = new KoTextParagCommand( + textdoc, undoRedoInfo.id, undoRedoInfo.eid, + undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout, + KoParagLayout::Tabulator); + textdoc->addCommand( cmd ); + undoRedoInfo.clear(); + emit showCursor(); + emit updateUI( true ); + return new KoTextCommand( this, /*cmd, */i18n("Change Tabulator") ); +} + +KCommand * KoTextObject::setParagDirectionCommand( KoTextCursor * cursor, QChar::Direction d, KoTextDocument::SelectionId selectionId ) +{ + if ( protectContent() ) + return 0L; + if ( !textdoc->hasSelection( selectionId, true ) && cursor && + cursor->parag()->direction() == d ) + return 0L; // No change needed. + + emit hideCursor(); + storeParagUndoRedoInfo( cursor, selectionId ); + + if ( !textdoc->hasSelection( selectionId, true ) && cursor ) { + cursor->parag()->setDirection( d ); + setLastFormattedParag( cursor->parag() ); + } + else + { + KoTextParag *start = textdoc->selectionStart( selectionId ); + KoTextParag *end = textdoc->selectionEnd( selectionId ); + setLastFormattedParag( start ); + for ( ; start && start != end->next() ; start = start->next() ) + start->setDirection( d ); + } + + formatMore( 2 ); + emit repaintChanged( this ); + ////// ### TODO +#if 0 + undoRedoInfo.newParagLayout.direction = d; + KoTextParagCommand *cmd = new KoTextParagCommand( + textdoc, undoRedoInfo.id, undoRedoInfo.eid, + undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout, + KoParagLayout::Shadow); + textdoc->addCommand( cmd ); +#endif + undoRedoInfo.clear(); + emit showCursor(); + emit updateUI( true ); +#if 0 + return new KoTextCommand( this, /*cmd, */i18n("Change Shadow") ); +#else + return 0L; +#endif +} + +void KoTextObject::removeSelectedText( KoTextCursor * cursor, KoTextDocument::SelectionId selectionId, const QString & cmdName, bool createUndoRedo ) +{ + if ( protectContent() ) + return ; + emit hideCursor(); + if( createUndoRedo) + { + checkUndoRedoInfo( cursor, UndoRedoInfo::RemoveSelected ); + if ( !undoRedoInfo.valid() ) { + textdoc->selectionStart( selectionId, undoRedoInfo.id, undoRedoInfo.index ); + undoRedoInfo.text = QString::null; + newPlaceHolderCommand( cmdName.isNull() ? i18n("Remove Selected Text") : cmdName ); + } + } + KoTextCursor c1 = textdoc->selectionStartCursor( selectionId ); + KoTextCursor c2 = textdoc->selectionEndCursor( selectionId ); + readFormats( c1, c2, true, true ); + //kdDebug(32500) << "KoTextObject::removeSelectedText text=" << undoRedoInfo.text.toString() << endl; + + textdoc->removeSelectedText( selectionId, cursor ); + + setLastFormattedParag( cursor->parag() ); + formatMore( 2 ); + emit repaintChanged( this ); + emit ensureCursorVisible(); + emit updateUI( true ); + emit showCursor(); + if(selectionId==KoTextDocument::Standard || selectionId==KoTextDocument::InputMethodPreedit) + selectionChangedNotify(); + if ( createUndoRedo) + undoRedoInfo.clear(); +} + +KCommand * KoTextObject::removeSelectedTextCommand( KoTextCursor * cursor, KoTextDocument::SelectionId selectionId, bool repaint ) +{ + if ( protectContent() ) + return 0L; + if ( !textdoc->hasSelection( selectionId, true ) ) + return 0L; + + undoRedoInfo.clear(); + textdoc->selectionStart( selectionId, undoRedoInfo.id, undoRedoInfo.index ); + Q_ASSERT( undoRedoInfo.id >= 0 ); + + KoTextCursor c1 = textdoc->selectionStartCursor( selectionId ); + KoTextCursor c2 = textdoc->selectionEndCursor( selectionId ); + readFormats( c1, c2, true, true ); + + textdoc->removeSelectedText( selectionId, cursor ); + + KMacroCommand *macroCmd = new KMacroCommand( i18n("Remove Selected Text") ); + + KoTextDocCommand *cmd = deleteTextCommand( textdoc, undoRedoInfo.id, undoRedoInfo.index, + undoRedoInfo.text.rawData(), + undoRedoInfo.customItemsMap, + undoRedoInfo.oldParagLayouts ); + textdoc->addCommand(cmd); + macroCmd->addCommand(new KoTextCommand( this, /*cmd, */QString::null )); + + if(!undoRedoInfo.customItemsMap.isEmpty()) + undoRedoInfo.customItemsMap.deleteAll( macroCmd ); + + undoRedoInfo.type = UndoRedoInfo::Invalid; // we don't want clear() to create a command + undoRedoInfo.clear(); + if ( repaint ) + selectionChangedNotify(); + return macroCmd; +} + +KCommand* KoTextObject::replaceSelectionCommand( KoTextCursor * cursor, const QString & replacement, + const QString & cmdName, + KoTextDocument::SelectionId selectionId, + int insertFlags, + CustomItemsMap customItemsMap ) +{ + if ( protectContent() ) + return 0L; + Q_ASSERT( ( insertFlags & DoNotRemoveSelected ) == 0 ); // nonsensical + const bool repaint = ( insertFlags & DoNotRepaint ) == 0; // DoNotRepaint is set during search/replace + if ( repaint ) + emit hideCursor(); + // This could be improved to use a macro command only when there's a selection to remove. + KMacroCommand * macroCmd = new KMacroCommand( cmdName ); + + // Remember formatting + KoTextCursor c1 = textdoc->selectionStartCursor( selectionId ); + KoTextFormat * format = c1.parag()->at( c1.index() )->format(); + format->addRef(); + + // Remove selected text, if any + KCommand* removeSelCmd = removeSelectedTextCommand( cursor, selectionId, repaint ); + if ( removeSelCmd ) + macroCmd->addCommand( removeSelCmd ); + + // Insert replacement + insert( cursor, format, + replacement, QString::null /* no place holder command */, + selectionId, insertFlags | DoNotRemoveSelected, customItemsMap ); + + KoTextDocCommand * cmd = new KoTextInsertCommand( textdoc, undoRedoInfo.id, undoRedoInfo.index, + undoRedoInfo.text.rawData(), + CustomItemsMap(), undoRedoInfo.oldParagLayouts ); + textdoc->addCommand( cmd ); + macroCmd->addCommand( new KoTextCommand( this, /*cmd, */QString::null ) ); + + undoRedoInfo.type = UndoRedoInfo::Invalid; // we don't want clear() to create a command + undoRedoInfo.clear(); + + format->removeRef(); + + setLastFormattedParag( c1.parag() ); + if ( repaint ) + { + formatMore( 2 ); + emit repaintChanged( this ); + emit ensureCursorVisible(); + emit updateUI( true ); + emit showCursor(); + if(selectionId==KoTextDocument::Standard) + selectionChangedNotify(); + } + return macroCmd; +} + +KCommand * KoTextObject::insertParagraphCommand( KoTextCursor *cursor ) +{ + if ( protectContent() ) + return 0L; + return replaceSelectionCommand( cursor, "\n", QString::null, KoTextDocument::Standard, CheckNewLine ); +} + +void KoTextObject::highlightPortion( KoTextParag * parag, int index, int length, bool repaint ) +{ + if ( !m_highlightSelectionAdded ) + { + textdoc->addSelection( KoTextDocument::HighlightSelection ); + textdoc->setSelectionColor( KoTextDocument::HighlightSelection, + QApplication::palette().color( QPalette::Active, QColorGroup::Dark ) ); + textdoc->setInvertSelectionText( KoTextDocument::HighlightSelection, true ); + m_highlightSelectionAdded = true; + } + + removeHighlight(repaint); // remove previous highlighted selection + KoTextCursor cursor( textdoc ); + cursor.setParag( parag ); + cursor.setIndex( index ); + textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor ); + cursor.setIndex( index + length ); + textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor ); + if ( repaint ) { + parag->setChanged( true ); + emit repaintChanged( this ); + } +} + +void KoTextObject::removeHighlight(bool repaint) +{ + if ( textdoc->hasSelection( KoTextDocument::HighlightSelection, true ) ) + { + KoTextParag * oldParag = textdoc->selectionStart( KoTextDocument::HighlightSelection ); + oldParag->setChanged( true ); + textdoc->removeSelection( KoTextDocument::HighlightSelection ); + } + if ( repaint ) + emit repaintChanged( this ); +} + +void KoTextObject::selectAll( bool select ) +{ + if ( !select ) + textdoc->removeSelection( KoTextDocument::Standard ); + else + textdoc->selectAll( KoTextDocument::Standard ); + selectionChangedNotify(); +} + +void KoTextObject::selectionChangedNotify( bool enableActions /* = true */) +{ + emit repaintChanged( this ); + if ( enableActions ) + emit selectionChanged( hasSelection() ); +} + +void KoTextObject::setViewArea( QWidget* w, int maxY ) +{ + m_mapViewAreas.replace( w, maxY ); +} + +void KoTextObject::setLastFormattedParag( KoTextParag *parag ) +{ + //kdDebug() << k_funcinfo << parag << " (" << ( parag ? QString::number(parag->paragId()) : QString("(null)") ) << ")" << endl; + if ( !m_lastFormatted || !parag || m_lastFormatted->paragId() >= parag->paragId() ) { + m_lastFormatted = parag; + } +} + +void KoTextObject::ensureFormatted( KoTextParag * parag, bool emitAfterFormatting /* = true */ ) +{ + if ( !textdoc->lastParag() ) + return; // safety test + //kdDebug(32500) << name() << " ensureFormatted " << parag->paragId() << endl; + if ( !parag->isValid() && m_lastFormatted == 0 ) + m_lastFormatted = parag; // bootstrap + + while ( !parag->isValid() ) + { + if ( !m_lastFormatted ) { + kdWarning() << "ensureFormatted for parag " << parag << " " << parag->paragId() << " still not formatted, but m_lastFormatted==0" << endl; + return; + } + // The paragid diff is "a good guess". The >=1 is a safety measure ;) + bool ret = formatMore( QMAX( 1, parag->paragId() - m_lastFormatted->paragId() ), emitAfterFormatting ); + if ( !ret ) { // aborted + //kdDebug(32500) << "ensureFormatted aborted!" << endl; + break; + } + } + //kdDebug(32500) << name() << " ensureFormatted " << parag->paragId() << " done" << endl; +} + +bool KoTextObject::formatMore( int count /* = 10 */, bool emitAfterFormatting /* = true */ ) +{ + if ( ( !m_lastFormatted && d->afterFormattingEmitted ) + || !m_visible || m_availableHeight == -1 ) + return false; + + if ( !textdoc->lastParag() ) + return false; // safety test + + if ( d->abortFormatting ) { + d->abortFormatting = false; + return false; + } + + if ( count == 0 ) + { + formatTimer->start( interval, TRUE ); + return true; + } + + int bottom = 0; + if ( m_lastFormatted ) + { + d->afterFormattingEmitted = false; + + int viewsBottom = 0; + QMapIterator<QWidget *, int> mapIt = m_mapViewAreas.begin(); + for ( ; mapIt != m_mapViewAreas.end() ; ++mapIt ) + viewsBottom = QMAX( viewsBottom, mapIt.data() ); + +#ifdef DEBUG_FORMAT_MORE + kdDebug(32500) << "formatMore " << name() + << " lastFormatted id=" << m_lastFormatted->paragId() + << " lastFormatted's top=" << m_lastFormatted->rect().top() + << " lastFormatted's height=" << m_lastFormatted->rect().height() + << " count=" << count << " viewsBottom=" << viewsBottom + << " availableHeight=" << m_availableHeight << endl; +#endif + if ( m_lastFormatted->prev() == 0 ) + { + emit formattingFirstParag(); +#ifdef TIMING_FORMAT + kdDebug(32500) << "formatMore " << name() << ". First parag -> starting timer" << endl; + m_time.start(); +#endif + } + + // Stop if we have formatted everything or if we need more space + // Otherwise, stop formatting after "to" paragraphs, + // but make sure we format everything the views need + int i; + for ( i = 0; + m_lastFormatted && bottom + m_lastFormatted->rect().height() <= m_availableHeight && + ( i < count || bottom <= viewsBottom ) ; ++i ) + { + KoTextParag* parag = m_lastFormatted; +#ifdef DEBUG_FORMAT_MORE + kdDebug(32500) << "formatMore formatting " << parag << " id=" << parag->paragId() << endl; +#endif + assert( parag->string() ); // i.e. not deleted + parag->format(); + bottom = parag->rect().top() + parag->rect().height(); +#if 0 //def DEBUG_FORMAT_MORE + kdDebug(32500) << "formatMore(inside) top=" << parag->rect().top() + << " height=" << parag->rect().height() + << " bottom=" << bottom << " m_lastFormatted(next parag) = " << m_lastFormatted->next() << endl; +#endif + + // Check for Head 1 (i.e. section) titles, and emit them - for the Section variable + if ( parag->counter() && parag->counter()->numbering() == KoParagCounter::NUM_CHAPTER + && parag->counter()->depth() == 0 ) + emit chapterParagraphFormatted( parag ); + + if ( d->abortFormatting ) { +#ifdef DEBUG_FORMAT_MORE + kdDebug(32500) << "formatMore formatting aborted. " << endl; +#endif + d->abortFormatting = false; + return false; + } + + if ( parag != m_lastFormatted ) + kdWarning() << "Some code changed m_lastFormatted during formatting! Was " << parag->paragId() << ", is now " << m_lastFormatted->paragId() << endl; +#if 0 // This happens now that formatting can 'abort' (e.g. due to page not yet created) + else if (!parag->isValid()) + kdWarning() << "PARAGRAPH " << parag->paragId() << " STILL INVALID AFTER FORMATTING" << endl; +#endif + m_lastFormatted = parag->next(); + } + } + else // formatting was done previously, but not emit afterFormatting + { + QRect rect = textdoc->lastParag()->rect(); + bottom = rect.top() + rect.height(); + } +#ifdef DEBUG_FORMAT_MORE + QString id; + if ( m_lastFormatted ) id = QString(" (%1)").arg(m_lastFormatted->paragId()); + kdDebug(32500) << "formatMore finished formatting. " + << " bottom=" << bottom + << " m_lastFormatted=" << m_lastFormatted << id + << endl; +#endif + + if ( emitAfterFormatting ) + { + d->afterFormattingEmitted = true; + bool needMoreSpace = false; + // Check if we need more space + if ( ( bottom > m_availableHeight ) || // this parag is already off page + ( m_lastFormatted && bottom + m_lastFormatted->rect().height() > m_availableHeight ) ) // or next parag will be off page + needMoreSpace = true; + // default value of 'abort' depends on need more space + bool abort = needMoreSpace; + emit afterFormatting( bottom, m_lastFormatted, &abort ); + if ( abort ) + return false; + else if ( needMoreSpace && m_lastFormatted ) // we got more space, keep formatting then + return formatMore( 2 ); + } + + // Now let's see when we'll need to get back here. + if ( m_lastFormatted ) + { + formatTimer->start( interval, TRUE ); +#ifdef DEBUG_FORMAT_MORE + kdDebug(32500) << name() << " formatMore: will have to format more. formatTimer->start with interval=" << interval << endl; +#endif + } + else + { + interval = QMAX( 0, interval ); +#ifdef DEBUG_FORMAT_MORE + kdDebug(32500) << name() << " formatMore: all formatted interval=" << interval << endl; +#endif +#ifdef TIMING_FORMAT + //if ( frameSetInfo() == FI_BODY ) + kdDebug(32500) << "formatMore: " << name() << " all formatted. Took " + << (double)(m_time.elapsed()) / 1000 << " seconds." << endl; +#endif + } + return true; +} + +void KoTextObject::abortFormatting() +{ + d->abortFormatting = true; +} + +void KoTextObject::doChangeInterval() +{ + //kdDebug(32500) << "KoTextObject::doChangeInterval back to interval=0" << endl; + interval = 0; +} + +void KoTextObject::typingStarted() +{ + //kdDebug(32500) << "KoTextObject::typingStarted" << endl; + changeIntervalTimer->stop(); + interval = 10; +} + +void KoTextObject::typingDone() +{ + changeIntervalTimer->start( 100, TRUE ); +} + + +// helper for changeCaseOfText +KCommand *KoTextObject::changeCaseOfTextParag( int cursorPosStart, int cursorPosEnd, + KoChangeCaseDia::TypeOfCase _type, + KoTextCursor *cursor, KoTextParag *parag ) +{ + if ( protectContent() ) + return 0L; + + KMacroCommand * macroCmd = new KMacroCommand( i18n("Change Case") ); + KoTextFormat *curFormat = parag->paragraphFormat(); + const QString text = parag->string()->toString().mid( cursorPosStart, cursorPosEnd - cursorPosStart ); + int posStart = cursorPosStart; + int posEnd = cursorPosStart; + KoTextCursor c1( textdoc ); + KoTextCursor c2( textdoc ); + //kdDebug() << k_funcinfo << "from " << cursorPosStart << " to " << cursorPosEnd << " (excluded). Parag=" << parag << " length=" << parag->length() << endl; + for ( int i = cursorPosStart; i < cursorPosEnd; ++i ) + { + KoTextStringChar & ch = *(parag->at(i)); + KoTextFormat * newFormat = ch.format(); + if( ch.isCustom() ) + { + posEnd = i; + c1.setParag( parag ); + c1.setIndex( posStart ); + c2.setParag( parag ); + c2.setIndex( posEnd ); + //kdDebug() << k_funcinfo << "found custom at " << i << " : doing from " << posStart << " to " << posEnd << endl; + + const QString repl = text.mid( posStart, posEnd - posStart ); + textdoc->setSelectionStart( KoTextDocument::Temp, &c1 ); + textdoc->setSelectionEnd( KoTextDocument::Temp, &c2 ); + macroCmd->addCommand(replaceSelectionCommand( + cursor, textChangedCase(repl,_type), + QString::null, KoTextDocument::Temp)); + do + { + ++i; + } + while( parag->at(i)->isCustom() && i != cursorPosEnd); + posStart=i; + posEnd=i; + } + else + { + if ( newFormat != curFormat ) + { + posEnd=i; + c1.setParag( parag ); + c1.setIndex( posStart ); + c2.setParag( parag ); + c2.setIndex( posEnd ); + //kdDebug() << k_funcinfo << "found new format at " << i << " : doing from " << posStart << " to " << posEnd << endl; + + const QString repl = text.mid( posStart, posEnd - posStart ); + textdoc->setSelectionStart( KoTextDocument::Temp, &c1 ); + textdoc->setSelectionEnd( KoTextDocument::Temp, &c2 ); + macroCmd->addCommand(replaceSelectionCommand( + cursor, textChangedCase(repl,_type), + QString::null, KoTextDocument::Temp)); + posStart=i; + posEnd=i; + curFormat = newFormat; + } + } + } + if ( posStart != cursorPosEnd ) + { + //kdDebug() << k_funcinfo << "finishing: doing from " << posStart << " to " << cursorPosEnd << endl; + c1.setParag( parag ); + c1.setIndex( posStart ); + c2.setParag( parag ); + c2.setIndex( cursorPosEnd ); + + textdoc->setSelectionStart( KoTextDocument::Temp, &c1 ); + textdoc->setSelectionEnd( KoTextDocument::Temp, &c2 ); + const QString repl = text.mid( posStart, cursorPosEnd - posStart ); + macroCmd->addCommand(replaceSelectionCommand( + cursor, textChangedCase(repl,_type), + QString::null, KoTextDocument::Temp)); + } + return macroCmd; + +} + +KCommand *KoTextObject::changeCaseOfText(KoTextCursor *cursor,KoChangeCaseDia::TypeOfCase _type) +{ + if ( protectContent() ) + return 0L; + KMacroCommand * macroCmd = new KMacroCommand( i18n("Change Case") ); + + KoTextCursor start = textdoc->selectionStartCursor( KoTextDocument::Standard ); + KoTextCursor end = textdoc->selectionEndCursor( KoTextDocument::Standard ); + emit hideCursor(); + + if ( start.parag() == end.parag() ) + { + int endIndex = QMIN( start.parag()->length() - 1, end.index() ); + macroCmd->addCommand( changeCaseOfTextParag( start.index(), endIndex, _type, + cursor, start.parag() ) ); + } + else + { + macroCmd->addCommand( changeCaseOfTextParag( start.index(), start.parag()->length() - 1, _type, + cursor, start.parag() ) ); + KoTextParag *p = start.parag()->next(); + while ( p && p != end.parag() ) + { + macroCmd->addCommand( changeCaseOfTextParag(0, p->length() - 1, _type, cursor, p ) ); + p = p->next(); + } + if ( p ) + { + int endIndex = QMIN( p->length() - 1, end.index() ); + macroCmd->addCommand( changeCaseOfTextParag(0, endIndex, _type, cursor, end.parag() )); + } + } + formatMore( 2 ); + emit repaintChanged( this ); + emit ensureCursorVisible(); + emit updateUI( true ); + emit showCursor(); + return macroCmd; +} + +QString KoTextObject::textChangedCase(const QString& _text,KoChangeCaseDia::TypeOfCase _type) +{ + QString text(_text); + switch(_type) + { + case KoChangeCaseDia::UpperCase: + text=text.upper(); + break; + case KoChangeCaseDia::LowerCase: + text=text.lower(); + break; + case KoChangeCaseDia::TitleCase: + for(uint i=0;i<text.length();i++) + { + if(text.at(i)!=' ') + { + QChar prev = text.at(QMAX(i-1,0)); + if(i==0 || prev == ' ' || prev == '\n' || prev == '\t') + text=text.replace(i, 1, text.at(i).upper() ); + else + text=text.replace(i, 1, text.at(i).lower() ); + } + } + break; + case KoChangeCaseDia::ToggleCase: + for(uint i=0;i<text.length();i++) + { + QString repl=QString(text.at(i)); + if(text.at(i)!=text.at(i).upper()) + repl=repl.upper(); + else if(text.at(i).lower()!=text.at(i)) + repl=repl.lower(); + text=text.replace(i, 1, repl ); + } + break; + case KoChangeCaseDia::SentenceCase: + for(uint i=0;i<text.length();i++) + { + if(text.at(i)!=' ') + { + QChar prev = text.at(QMAX(i-1,0)); + if(i==0 || prev == '\n' ||prev.isPunct()) + text=text.replace(i, 1, text.at(i).upper() ); + } + } + break; + default: + kdDebug(32500)<<"Error in changeCaseOfText !\n"; + break; + + } + return text; +} + +// Warning, this doesn't ref the format! +KoTextFormat * KoTextObject::currentFormat() const +{ + // We use the formatting of the very first character + // Should we use a style instead, maybe ? + KoTextStringChar *ch = textdoc->firstParag()->at( 0 ); + return ch->format(); +} + +const KoParagLayout * KoTextObject::currentParagLayoutFormat() const +{ + KoTextParag * parag = textdoc->firstParag(); + return &(parag->paragLayout()); +} + +bool KoTextObject::rtl() const +{ + return textdoc->firstParag()->string()->isRightToLeft(); +} + +void KoTextObject::loadOasisContent( const QDomElement &bodyElem, KoOasisContext& context, KoStyleCollection * styleColl ) +{ + textDocument()->clear(false); // Get rid of dummy paragraph (and more if any) + setLastFormattedParag( 0L ); // no more parags, avoid UMR in next setLastFormattedParag call + + KoTextParag *lastParagraph = textDocument()->loadOasisText( bodyElem, context, 0, styleColl, 0 ); + + if ( !lastParagraph ) // We created no paragraph + { + // Create an empty one, then. See KoTextDocument ctor. + textDocument()->clear( true ); + textDocument()->firstParag()->setStyle( styleColl->findStyle( "Standard" ) ); + } + else + textDocument()->setLastParag( lastParagraph ); + + setLastFormattedParag( textDocument()->firstParag() ); +} + +KoTextCursor KoTextObject::pasteOasisText( const QDomElement &bodyElem, KoOasisContext& context, + KoTextCursor& cursor, KoStyleCollection * styleColl ) +{ + KoTextCursor resultCursor( cursor ); + KoTextParag* lastParagraph = cursor.parag(); + bool removeNewline = false; + uint pos = cursor.index(); + if ( pos == 0 && lastParagraph->length() <= 1 ) { + // Pasting on an empty paragraph -> respect <text:h> in selected text etc. + lastParagraph = lastParagraph->prev(); + lastParagraph = textDocument()->loadOasisText( bodyElem, context, lastParagraph, styleColl, cursor.parag() ); + if ( lastParagraph ) { + resultCursor.setParag( lastParagraph ); + resultCursor.setIndex( lastParagraph->length() - 1 ); + } + removeNewline = true; + } else { + // Pasting inside a non-empty paragraph -> insert/append text to it. + // This loop looks for the *FIRST* paragraph only. + for ( QDomNode text (bodyElem.firstChild()); !text.isNull(); text = text.nextSibling() ) + { + QDomElement tag = text.toElement(); + if ( tag.isNull() ) continue; + // The first tag could be a text:p, text:h, text:numbered-paragraph, but also + // a frame (anchored to page), a TOC, etc. For those, don't try to concat-with-existing-paragraph. + // For text:numbered-paragraph, find the text:p or text:h inside it. + QDomElement tagToLoad = tag; + if ( tag.localName() == "numbered-paragraph" ) { + QDomElement npchild; + forEachElement( npchild, tag ) + { + if ( npchild.localName() == "p" || npchild.localName() == "h" ) { + tagToLoad = npchild; + break; + } + } + } + + if ( tagToLoad.localName() == "p" || tagToLoad.localName() == "h" ) { + context.styleStack().save(); + context.fillStyleStack( tagToLoad, KoXmlNS::text, "style-name", "paragraph" ); + + // OO.o compatibility: ignore leading whitespace in <p> and <h> elements + lastParagraph->loadOasisSpan( tagToLoad, context, pos, true ); + context.styleStack().restore(); + + lastParagraph->setChanged( true ); + lastParagraph->invalidate( 0 ); + + // Now split this parag, to make room for the next paragraphs + resultCursor.setParag( lastParagraph ); + resultCursor.setIndex( pos ); + resultCursor.splitAndInsertEmptyParag( FALSE, TRUE ); + removeNewline = true; + + // Done with first parag, remove it and exit loop + const_cast<QDomElement &>( bodyElem ).removeChild( tag ); // somewhat hackish + } + break; + } + resultCursor.setParag( lastParagraph ); + resultCursor.setIndex( pos ); + // Load the rest the usual way. + lastParagraph = textDocument()->loadOasisText( bodyElem, context, lastParagraph, styleColl, lastParagraph->next() ); + if ( lastParagraph != resultCursor.parag() ) // we loaded more paragraphs + { + removeNewline = true; + resultCursor.setParag( lastParagraph ); + resultCursor.setIndex( lastParagraph->length() - 1 ); + } + } + KoTextParag* p = resultCursor.parag(); + if ( p ) p = p->next(); + // Remove the additional newline that loadOasisText inserted + if ( removeNewline && resultCursor.remove() ) { + if ( m_lastFormatted == p ) { // has been deleted + m_lastFormatted = resultCursor.parag(); + } + } + return resultCursor; +} + +void KoTextObject::saveOasisContent( KoXmlWriter& writer, KoSavingContext& context ) const +{ + textDocument()->saveOasisContent( writer, context ); +} + +KCommand *KoTextObject::setParagLayoutFormatCommand( KoParagLayout *newLayout,int flags,int marginIndex) +{ + if ( protectContent() ) + return 0L; + textdoc->selectAll( KoTextDocument::Temp ); + KoTextCursor *cursor = new KoTextCursor( textdoc ); + KCommand* cmd = setParagLayoutCommand( cursor, *newLayout, KoTextDocument::Temp, + flags, marginIndex, true /*createUndoRedo*/ ); + textdoc->removeSelection( KoTextDocument::Temp ); + delete cursor; + return cmd; +} + +void KoTextObject::setFormat( KoTextFormat * newFormat, int flags, bool zoomFont ) +{ + if ( protectContent() ) + return ; + // This version of setFormat works on the whole textobject - we use the Temp selection for that + textdoc->selectAll( KoTextDocument::Temp ); + KCommand *cmd = setFormatCommand( 0L, 0L, newFormat, + flags, zoomFont, KoTextDocument::Temp ); + textdoc->removeSelection( KoTextDocument::Temp ); + if (cmd) + emit newCommand( cmd ); + + KoTextFormat format = *currentFormat(); + //format.setPointSize( docFontSize( currentFormat() ) ); // "unzoom" the font size + emit showFormatObject(format); +} + +KCommand *KoTextObject::setChangeCaseOfTextCommand(KoChangeCaseDia::TypeOfCase _type) +{ + if ( protectContent() ) + return 0L; + textdoc->selectAll( KoTextDocument::Standard ); + KoTextCursor *cursor = new KoTextCursor( textdoc ); + KCommand* cmd = changeCaseOfText(cursor, _type); + textdoc->removeSelection( KoTextDocument::Standard ); + delete cursor; + return cmd; +} + +void KoTextObject::setNeedSpellCheck(bool b) +{ + m_needsSpellCheck = b; + for (KoTextParag * parag = textdoc->firstParag(); parag ; parag = parag->next()) + parag->string()->setNeedsSpellCheck( b ); +} + +bool KoTextObject::statistics( QProgressDialog *progress, ulong & charsWithSpace, ulong & charsWithoutSpace, ulong & words, ulong & sentences, ulong & syllables, ulong & lines, bool selected ) +{ + // parts of words for better counting of syllables: + // (only use reg exp if necessary -> speed up) + + QStringList subs_syl; + subs_syl << "cial" << "tia" << "cius" << "cious" << "giu" << "ion" << "iou"; + QStringList subs_syl_regexp; + subs_syl_regexp << "sia$" << "ely$"; + + QStringList add_syl; + add_syl << "ia" << "riet" << "dien" << "iu" << "io" << "ii"; + QStringList add_syl_regexp; + add_syl_regexp << "[aeiouym]bl$" << "[aeiou]{3}" << "^mc" << "ism$" + << "[^l]lien" << "^coa[dglx]." << "[^gq]ua[^auieo]" << "dnt$"; + + QString s; + KoTextParag * parag = textdoc->firstParag(); + for ( ; parag ; parag = parag->next() ) + { + if ( progress ) + { + progress->setProgress(progress->progress()+1); + // MA: resizing if KWStatisticsDialog does not work correct with this enabled, don't know why + kapp->processEvents(); + if ( progress->wasCancelled() ) + return false; + } + // start of a table +/* if ( parag->at(0)->isCustom()) + { + KoLinkVariable *var=dynamic_cast<KoLinkVariable *>(parag->at(0)->customItem()); + if(!var) + continue; + }*/ + bool hasTrailingSpace = true; + if ( !selected ) { + s = parag->string()->toString(); + lines += parag->lines(); + } else { + if ( parag->hasSelection( KoTextDocument::Standard ) ) { + hasTrailingSpace = false; + s = parag->string()->toString(); + if ( !( parag->fullSelected( KoTextDocument::Standard ) ) ) { + s = s.mid( parag->selectionStart( KoTextDocument::Standard ), parag->selectionEnd( KoTextDocument::Standard ) - parag->selectionStart( KoTextDocument::Standard ) ); + lines+=numberOfparagraphLineSelected(parag); + } + else + lines += parag->lines(); + } else { + continue; + } + } + + // Character count + for ( uint i = 0 ; i < s.length() - ( hasTrailingSpace ? 1 : 0 ) ; ++i ) + { + QChar ch = s[i]; + ++charsWithSpace; + if ( !ch.isSpace() ) + ++charsWithoutSpace; + } + + // Syllable and Word count + // Algorithm mostly taken from Greg Fast's Lingua::EN::Syllable module for Perl. + // This guesses correct for 70-90% of English words, but the overall value + // is quite good, as some words get a number that's too high and others get + // one that's too low. + // IMPORTANT: please test any changes against demos/statistics.kwd + QRegExp re("\\s+"); + QStringList wordlist = QStringList::split(re, s); + words += wordlist.count(); + re.setCaseSensitive(false); + for ( QStringList::Iterator it = wordlist.begin(); it != wordlist.end(); ++it ) { + QString word = *it; + re.setPattern("[!?.,:_\"-]"); // clean word from punctuation + word.remove(re); + if ( word.length() <= 3 ) { // extension to the original algorithm + syllables++; + continue; + } + re.setPattern("e$"); + word.remove(re); + re.setPattern("[^aeiouy]+"); + QStringList syls = QStringList::split(re, word); + int word_syllables = 0; + for ( QStringList::Iterator it = subs_syl.begin(); it != subs_syl.end(); ++it ) { + if( word.find(*it, 0, false) != -1 ) + word_syllables--; + } + for ( QStringList::Iterator it = subs_syl_regexp.begin(); it != subs_syl_regexp.end(); ++it ) { + re.setPattern(*it); + if( word.find(re) != -1 ) + word_syllables--; + } + for ( QStringList::Iterator it = add_syl.begin(); it != add_syl.end(); ++it ) { + if( word.find(*it, 0, false) != -1 ) + word_syllables++; + } + for ( QStringList::Iterator it = add_syl_regexp.begin(); it != add_syl_regexp.end(); ++it ) { + re.setPattern(*it); + if( word.find(re) != -1 ) + word_syllables++; + } + word_syllables += syls.count(); + if ( word_syllables == 0 ) + word_syllables = 1; + syllables += word_syllables; + } + re.setCaseSensitive(true); + + // Sentence count + // Clean up for better result, destroys the original text but we only want to count + s = s.stripWhiteSpace(); + QChar lastchar = s.at(s.length()); + if( ! s.isEmpty() && ! KoAutoFormat::isMark( lastchar ) ) { // e.g. for headlines + s = s + "."; + } + re.setPattern("[.?!]+"); // count "..." as only one "." + s.replace(re, "."); + re.setPattern("\\d\\.\\d"); // don't count floating point numbers as sentences + s.replace(re, "0,0"); + re.setPattern("[A-Z]\\.+"); // don't count "U.S.A." as three sentences + s.replace(re, "*"); + for ( uint i = 0 ; i < s.length() ; ++i ) + { + QChar ch = s[i]; + if ( KoAutoFormat::isMark( ch ) ) + ++sentences; + } + } + return true; +} + +int KoTextObject::numberOfparagraphLineSelected( KoTextParag *parag) +{ + int indexOfLineStart; + int lineStart; + int lineEnd; + KoTextCursor c1 = textdoc->selectionStartCursor( KoTextDocument::Standard ); + KoTextCursor c2 = textdoc->selectionEndCursor( KoTextDocument::Standard ); + parag->lineStartOfChar( c1.index(), &indexOfLineStart, &lineStart ); + + parag->lineStartOfChar( c2.index(), &indexOfLineStart, &lineEnd ); + return (lineEnd - lineStart+1); +} + +KoVariable* KoTextObject::variableAtPoint( const QPoint& iPoint ) const +{ + KoTextCursor cursor( textDocument() ); + int variablePosition = -1; + cursor.place( iPoint, textDocument()->firstParag(), false, &variablePosition ); + if ( variablePosition == -1 ) + return 0; + return variableAtPosition( cursor.parag(), variablePosition ); +} + +KoVariable* KoTextObject::variableAtPosition( KoTextParag* parag, int index ) const +{ + KoTextStringChar * ch = parag->at( index ); + if ( ch->isCustom() ) + return dynamic_cast<KoVariable *>( ch->customItem() ); + return 0; +} + +const char * KoTextObject::acceptSelectionMimeType() +{ + return "application/vnd.oasis.opendocument."; +} + +QCString KoTextObject::providesOasis( QMimeSource* mime ) +{ + const char* fmt; + const char* acceptMimeType = acceptSelectionMimeType(); + for ( int i = 0; (fmt = mime->format(i)); ++i ) + { + if ( QString( fmt ).startsWith( acceptMimeType ) ) + { + return fmt; + } + } + return ""; +} + +#ifndef NDEBUG +void KoTextObject::printRTDebug(int info) +{ + KoTextParag* lastParag = 0; + for (KoTextParag * parag = textdoc->firstParag(); parag ; parag = parag->next()) + { + assert( parag->prev() == lastParag ); + parag->printRTDebug( info ); + lastParag = parag; + } + if ( info == 1 ) + textdoc->formatCollection()->debug(); +} +#endif + +////// Implementation of the KoTextFormatInterface "interface" + +KCommand *KoTextFormatInterface::setBoldCommand(bool on) +{ + KoTextFormat format( *currentFormat() ); + format.setBold( on ); + return setFormatCommand( &format, KoTextFormat::Bold ); +} + +KCommand *KoTextFormatInterface::setItalicCommand( bool on) +{ + KoTextFormat format( *currentFormat() ); + format.setItalic( on ); + return setFormatCommand( &format, KoTextFormat::Italic ); +} + +KCommand *KoTextFormatInterface::setUnderlineCommand( bool on ) +{ + KoTextFormat format( *currentFormat() ); + format.setUnderlineType( on ? KoTextFormat::U_SIMPLE : KoTextFormat::U_NONE); + return setFormatCommand( &format, KoTextFormat::ExtendUnderLine ); +} + +KCommand *KoTextFormatInterface::setUnderlineColorCommand( const QColor &color ) +{ + KoTextFormat format( *currentFormat() ); + format.setTextUnderlineColor( color); + return setFormatCommand( &format, KoTextFormat::ExtendUnderLine ); +} + +KCommand *KoTextFormatInterface::setDoubleUnderlineCommand( bool on ) +{ + KoTextFormat format( *currentFormat() ); + format.setUnderlineType( on ? KoTextFormat::U_DOUBLE : KoTextFormat::U_NONE); + return setFormatCommand( &format, KoTextFormat::ExtendUnderLine ); +} + +KCommand *KoTextFormatInterface::setStrikeOutCommand( bool on ) +{ + KoTextFormat format( *currentFormat() ); + format.setStrikeOutType( on ? KoTextFormat::S_SIMPLE : KoTextFormat::S_NONE); + return setFormatCommand( &format, KoTextFormat::StrikeOut ); +} + +KCommand *KoTextFormatInterface::setTextBackgroundColorCommand(const QColor & col) +{ + KoTextFormat format( *currentFormat() ); + format.setTextBackgroundColor( col ); + return setFormatCommand( &format, KoTextFormat::TextBackgroundColor ); +} + +KCommand *KoTextFormatInterface::setPointSizeCommand( int s ) +{ + KoTextFormat format( *currentFormat() ); + format.setPointSize( s ); + return setFormatCommand( &format, KoTextFormat::Size, true /* zoom the font size */ ); +} + +KCommand *KoTextFormatInterface::setFamilyCommand(const QString &font) +{ + KoTextFormat format( *currentFormat() ); + format.setFamily( font ); + return setFormatCommand( &format, KoTextFormat::Family ); +} + +QColor KoTextFormatInterface::textBackgroundColor() const +{ + return currentFormat()->textBackgroundColor(); +} + +QColor KoTextFormatInterface::textUnderlineColor()const +{ + return currentFormat()->textUnderlineColor(); +} + +QColor KoTextFormatInterface::textColor() const +{ + return currentFormat()->color(); +} + +bool KoTextFormatInterface::textUnderline()const +{ + return currentFormat()->underline(); +} + +bool KoTextFormatInterface::textDoubleUnderline()const +{ + return currentFormat()->doubleUnderline(); +} + +bool KoTextFormatInterface::textBold()const +{ + return currentFormat()->font().bold(); +} + +bool KoTextFormatInterface::textStrikeOut()const +{ + return currentFormat()->font().strikeOut(); +} + +bool KoTextFormatInterface::textItalic() const +{ + return currentFormat()->font().italic(); +} + +bool KoTextFormatInterface::textSubScript() const +{ + return (currentFormat()->vAlign()==KoTextFormat::AlignSubScript); +} + +bool KoTextFormatInterface::textSuperScript() const +{ + return (currentFormat()->vAlign()==KoTextFormat::AlignSuperScript); +} + +double KoTextFormatInterface::shadowDistanceX() const +{ + return currentFormat()->shadowDistanceX(); +} + +double KoTextFormatInterface::shadowDistanceY() const +{ + return currentFormat()->shadowDistanceY(); +} + +QColor KoTextFormatInterface::shadowColor() const +{ + return currentFormat()->shadowColor(); +} + +KoTextFormat::AttributeStyle KoTextFormatInterface::fontAttribute() const +{ + return currentFormat()->attributeFont(); +} + +double KoTextFormatInterface::relativeTextSize() const +{ + return currentFormat()->relativeTextSize(); +} + +int KoTextFormatInterface::offsetFromBaseLine()const +{ + return currentFormat()->offsetFromBaseLine(); +} + +bool KoTextFormatInterface::wordByWord()const +{ + return currentFormat()->wordByWord(); +} + +bool KoTextFormatInterface::hyphenation()const +{ + return currentFormat()->hyphenation(); +} + +KoTextFormat::UnderlineType KoTextFormatInterface::underlineType()const +{ + return currentFormat()->underlineType(); +} + +KoTextFormat::StrikeOutType KoTextFormatInterface::strikeOutType()const +{ + return currentFormat()->strikeOutType(); +} + +KoTextFormat::UnderlineStyle KoTextFormatInterface::underlineStyle()const +{ + return currentFormat()->underlineStyle(); +} + +KoTextFormat::StrikeOutStyle KoTextFormatInterface::strikeOutStyle()const +{ + return currentFormat()->strikeOutStyle(); +} + +QFont KoTextFormatInterface::textFont() const +{ + QFont fn( currentFormat()->font() ); + // "unzoom" the font size + //fn.setPointSize( static_cast<int>( KoTextZoomHandler::layoutUnitPtToPt( fn.pointSize() ) ) ); + return fn; +} + +QString KoTextFormatInterface::textFontFamily()const +{ + return currentFormat()->font().family(); +} + +QString KoTextFormatInterface::language() const +{ + return currentFormat()->language(); +} + +KCommand *KoTextFormatInterface::setTextColorCommand(const QColor &color) +{ + KoTextFormat format( *currentFormat() ); + format.setColor( color ); + return setFormatCommand( &format, KoTextFormat::Color ); +} + +KCommand *KoTextFormatInterface::setTextSubScriptCommand(bool on) +{ + KoTextFormat format( *currentFormat() ); + if(!on) + format.setVAlign(KoTextFormat::AlignNormal); + else + format.setVAlign(KoTextFormat::AlignSubScript); + return setFormatCommand( &format, KoTextFormat::VAlign ); +} + +KCommand *KoTextFormatInterface::setTextSuperScriptCommand(bool on) +{ + KoTextFormat format( *currentFormat() ); + if(!on) + format.setVAlign(KoTextFormat::AlignNormal); + else + format.setVAlign(KoTextFormat::AlignSuperScript); + return setFormatCommand( &format, KoTextFormat::VAlign ); +} + +KCommand *KoTextFormatInterface::setDefaultFormatCommand() +{ + KoTextFormatCollection * coll = currentFormat()->parent(); + Q_ASSERT(coll); + if(coll) + { + KoTextFormat * format = coll->defaultFormat(); + return setFormatCommand( format, KoTextFormat::Format ); + } else { + kdDebug() << "useless call to setDefaultFormatCommand at: " << kdBacktrace() << endl; + } + return 0; +} + +KCommand *KoTextFormatInterface::setAlignCommand(int align) +{ + KoParagLayout format( *currentParagLayoutFormat() ); + format.alignment=align; + return setParagLayoutFormatCommand(&format,KoParagLayout::Alignment); +} + +KCommand *KoTextFormatInterface::setHyphenationCommand( bool _b ) +{ + KoTextFormat format( *currentFormat() ); + format.setHyphenation( _b ); + return setFormatCommand( &format, KoTextFormat::Hyphenation); +} + + +KCommand *KoTextFormatInterface::setShadowTextCommand( double shadowDistanceX, double shadowDistanceY, const QColor& shadowColor ) +{ + KoTextFormat format( *currentFormat() ); + format.setShadow( shadowDistanceX, shadowDistanceY, shadowColor ); + return setFormatCommand( &format, KoTextFormat::ShadowText ); +} + +KCommand *KoTextFormatInterface::setFontAttributeCommand( KoTextFormat::AttributeStyle _att) +{ + KoTextFormat format( *currentFormat() ); + format.setAttributeFont( _att ); + return setFormatCommand( &format, KoTextFormat::Attribute ); +} + +KCommand *KoTextFormatInterface::setRelativeTextSizeCommand( double _size ) +{ + KoTextFormat format( *currentFormat() ); + format.setRelativeTextSize( _size ); + return setFormatCommand( &format, KoTextFormat::VAlign ); +} + +KCommand *KoTextFormatInterface::setOffsetFromBaseLineCommand( int _offset ) +{ + KoTextFormat format( *currentFormat() ); + format.setOffsetFromBaseLine( _offset ); + return setFormatCommand( &format, KoTextFormat::OffsetFromBaseLine ); +} + +KCommand *KoTextFormatInterface::setWordByWordCommand( bool _b ) +{ + KoTextFormat format( *currentFormat() ); + format.setWordByWord( _b ); + return setFormatCommand( &format, KoTextFormat::WordByWord ); +} + +#if 0 +void KoTextFormatInterface::setAlign(int align) +{ + KCommand *cmd = setAlignCommand( align ); + emitNewCommand( cmd ); +} + +void KoTextFormatInterface::setMargin(QStyleSheetItem::Margin m, double margin) +{ + KCommand *cmd = setMarginCommand( m, margin ); + emitNewCommand( cmd ); +} + +void KoTextFormatInterface::setTabList(const KoTabulatorList & tabList ) +{ + KCommand *cmd = setTabListCommand( tabList ); + emitNewCommand( cmd ); +} + +void KoTextFormatInterface::setCounter(const KoParagCounter & counter ) +{ + KCommand *cmd = setCounterCommand( counter ); + emitNewCommand( cmd ); +} + +void KoTextFormatInterface::setParagLayoutFormat( KoParagLayout *newLayout, int flags, int marginIndex) +{ + KCommand *cmd = setParagLayoutFormatCommand(newLayout, flags, marginIndex); + emitNewCommand( cmd ); +} +#endif + +KCommand *KoTextFormatInterface::setMarginCommand(QStyleSheetItem::Margin m, double margin) +{ + KoParagLayout format( *currentParagLayoutFormat() ); + format.margins[m]=margin; + return setParagLayoutFormatCommand(&format,KoParagLayout::Margins,(int)m); +} + +KCommand *KoTextFormatInterface::setTabListCommand(const KoTabulatorList & tabList ) + { + KoParagLayout format( *currentParagLayoutFormat() ); + format.setTabList(tabList); + return setParagLayoutFormatCommand(&format,KoParagLayout::Tabulator); +} + +KCommand *KoTextFormatInterface::setCounterCommand(const KoParagCounter & counter ) +{ + KoParagLayout format( *currentParagLayoutFormat() ); + if(!format.counter) + format.counter = new KoParagCounter; + *format.counter = counter; + return setParagLayoutFormatCommand(&format,KoParagLayout::BulletNumber); +} + +KCommand *KoTextFormatInterface::setLanguageCommand(const QString &_lang) +{ + KoTextFormat format( *currentFormat() ); + format.setLanguage(_lang); + return setFormatCommand( &format, KoTextFormat::Language ); +} + +KoTextDocCommand *KoTextFormatInterface::deleteTextCommand( KoTextDocument *textdoc, int id, int index, const QMemArray<KoTextStringChar> & str, const CustomItemsMap & customItemsMap, const QValueList<KoParagLayout> & oldParagLayouts ) +{ + return textdoc->deleteTextCommand( textdoc, id, index, str, customItemsMap, oldParagLayouts ); +} + +#include "KoTextObject.moc" |