From 8362bf63dea22bbf6736609b0f49c152f975eb63 Mon Sep 17 00:00:00 2001 From: tpearson Date: Wed, 20 Jan 2010 01:29:50 +0000 Subject: Added old abandoned KDE3 version of koffice git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- lib/kotext/KoTextIterator.cpp | 389 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 389 insertions(+) create mode 100644 lib/kotext/KoTextIterator.cpp (limited to 'lib/kotext/KoTextIterator.cpp') diff --git a/lib/kotext/KoTextIterator.cpp b/lib/kotext/KoTextIterator.cpp new file mode 100644 index 00000000..87a90fb3 --- /dev/null +++ b/lib/kotext/KoTextIterator.cpp @@ -0,0 +1,389 @@ +/* This file is part of the KDE project + Copyright (C) 2002-2006 David Faure + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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 "KoTextIterator.h" +#include "KoTextParag.h" +#include "KoTextView.h" +#include +#include +#include + +//#define DEBUG_ITERATOR + +/** + * The search direction (forward or backward) is handled in a bit of a tricky way. + * m_firstParag/m_firstIndex is where the search starts, whichever the direction + * m_lastParag/m_lastIndex is where the search ends, whichever the direction + * But the list of textobjects is as given (we assume document order). + * So we go from the first to the last textobject, or from the last to the first textobject. + */ + +void KoTextIterator::init( const QValueList & lstObjects, KoTextView* textView, int options ) +{ + Q_ASSERT( !lstObjects.isEmpty() ); + + m_lstObjects.clear(); + m_firstParag = 0; + m_firstIndex = 0; + m_options = options; + + // 'From Cursor' option + if ( options & KFindDialog::FromCursor ) + { + if ( textView ) { + m_firstParag = textView->cursor()->parag(); + m_firstIndex = textView->cursor()->index(); + } else { + // !? FromCursor option can't work + m_options &= ~KFindDialog::FromCursor; + kdWarning(32500) << "FromCursor specified, but no textview?" << endl; + } + } // no else here ! + + bool forw = ! ( options & KFindDialog::FindBackwards ); + + // 'Selected Text' option + if ( textView && ( options & KFindDialog::SelectedText ) ) + { + KoTextObject* textObj = textView->textObject(); + KoTextCursor c1 = textObj->textDocument()->selectionStartCursor( KoTextDocument::Standard ); + KoTextCursor c2 = textObj->textDocument()->selectionEndCursor( KoTextDocument::Standard ); + if ( !m_firstParag ) // not from cursor + { + m_firstParag = forw ? c1.parag() : c2.parag(); + m_firstIndex = forw ? c1.index() : c2.index(); + } + m_lastParag = forw ? c2.parag() : c1.parag(); + m_lastIndex = forw ? c2.index() : c1.index(); + // Find in the selection only -> only one textobject + m_lstObjects.append( textObj ); + m_currentTextObj = m_lstObjects.begin(); + } + else + { + // Not "selected text" -> loop through all textobjects + m_lstObjects = lstObjects; + if ( textView && (options & KFindDialog::FromCursor) ) + { + KoTextObject* initialFirst = m_lstObjects.first(); + // textView->textObject() should be first in m_lstObjects (last when going backwards) ! + // Let's ensure this is the case, but without changing the order of the objects. + if ( forw ) { + while( m_lstObjects.first() != textView->textObject() ) { + KoTextObject* textobj = m_lstObjects.front(); + m_lstObjects.pop_front(); + m_lstObjects.push_back( textobj ); + if ( m_lstObjects.first() == initialFirst ) { // safety + kdWarning(32500) << "Didn't manage to find " << textView->textObject() << " in the list of textobjects!!!" << endl; + break; + } + } + } else { + while( m_lstObjects.last() != textView->textObject() ) { + KoTextObject* textobj = m_lstObjects.back(); + m_lstObjects.pop_back(); + m_lstObjects.push_front( textobj ); + if ( m_lstObjects.first() == initialFirst ) { // safety + kdWarning(32500) << "Didn't manage to find " << textView->textObject() << " in the list of textobjects!!!" << endl; + break; + } + } + } + } + + KoTextParag* firstParag = m_lstObjects.first()->textDocument()->firstParag(); + int firstIndex = 0; + KoTextParag* lastParag = m_lstObjects.last()->textDocument()->lastParag(); + int lastIndex = lastParag->length()-1; + if ( !m_firstParag ) // only set this when not 'from cursor'. + { + m_firstParag = forw ? firstParag : lastParag; + m_firstIndex = forw ? firstIndex : lastIndex; + } + // always set the ending point + m_lastParag = forw ? lastParag : firstParag; + m_lastIndex = forw ? lastIndex : firstIndex; + m_currentTextObj = forw ? m_lstObjects.begin() : m_lstObjects.fromLast(); + } + + assert( *m_currentTextObj ); // all branches set it + assert( m_firstParag ); + assert( m_lastParag ); + Q_ASSERT( (*m_currentTextObj)->isVisible() ); + m_currentParag = m_firstParag; +#ifdef DEBUG_ITERATOR + kdDebug(32500) << "KoTextIterator::init from(" << *m_currentTextObj << "," << m_firstParag->paragId() << ") - to(" << (forw?m_lstObjects.last():m_lstObjects.first()) << "," << m_lastParag->paragId() << "), " << m_lstObjects.count() << " textObjects." << endl; + QValueList::Iterator it = m_lstObjects.begin(); + for( ; it != m_lstObjects.end(); ++it ) + kdDebug(32500) << (*it) << " " << (*it)->name() << endl; +#endif + Q_ASSERT( (*m_currentTextObj)->textDocument() == m_currentParag->textDocument() ); + Q_ASSERT( (forw?m_lstObjects.last():m_lstObjects.first())->textDocument() == m_lastParag->textDocument() ); + + connectTextObjects(); +} + +void KoTextIterator::restart() +{ + if( m_lstObjects.isEmpty() ) + return; + m_currentParag = m_firstParag; + bool forw = ! ( m_options & KFindDialog::FindBackwards ); + Q_ASSERT( ! (m_options & KFindDialog::FromCursor) ); // doesn't make much sense to keep it, right? + if ( (m_options & KFindDialog::FromCursor) || forw ) + m_currentTextObj = m_lstObjects.begin(); + else + m_currentTextObj = m_lstObjects.fromLast(); + if ( !(*m_currentTextObj)->isVisible() ) + nextTextObject(); +#ifdef DEBUG_ITERATOR + if ( m_currentParag ) + kdDebug(32500) << "KoTextIterator::restart from(" << *m_currentTextObj << "," << m_currentParag->paragId() << ") - to(" << (forw?m_lstObjects.last():m_lstObjects.first()) << "," << m_lastParag->paragId() << "), " << m_lstObjects.count() << " textObjects." << endl; + else + kdDebug(32500) << "KoTextIterator::restart - nowhere to go!" << endl; +#endif +} + +void KoTextIterator::connectTextObjects() +{ + QValueList::Iterator it = m_lstObjects.begin(); + for( ; it != m_lstObjects.end(); ++it ) { + connect( (*it), SIGNAL( paragraphDeleted( KoTextParag* ) ), + this, SLOT( slotParagraphDeleted( KoTextParag* ) ) ); + connect( (*it), SIGNAL( paragraphModified( KoTextParag*, int, int, int ) ), + this, SLOT( slotParagraphModified( KoTextParag*, int, int, int ) ) ); + // We don't connect to destroyed(), because for undo/redo purposes, + // we never really delete textdocuments nor textobjects. + // So this is never called. + // Instead the textobject is simply set to invisible, and this is handled by nextTextObject + } +} + +void KoTextIterator::slotParagraphModified( KoTextParag* parag, int modifyType, int pos, int length ) +{ + if ( parag == m_currentParag ) + emit currentParagraphModified( modifyType, pos, length ); +} + +void KoTextIterator::slotParagraphDeleted( KoTextParag* parag ) +{ +#ifdef DEBUG_ITERATOR + kdDebug(32500) << "KoTextIterator::slotParagraphDeleted " << parag << " (" << parag->paragId() << ")" << endl; +#endif + // Note that the direction doesn't matter here. A begin/end + // at end of parag N or at beginning of parag N+1 is the same, + // and m_firstIndex/m_lastIndex becomes irrelevant, anyway. + if ( parag == m_lastParag ) + { + if ( m_lastParag->prev() ) { + m_lastParag = m_lastParag->prev(); + m_lastIndex = m_lastParag->length()-1; + } else { + m_lastParag = m_lastParag->next(); + m_lastIndex = 0; + } + } + if ( parag == m_firstParag ) + { + if ( m_firstParag->prev() ) { + m_firstParag = m_firstParag->prev(); + m_firstIndex = m_firstParag->length()-1; + } else { + m_firstParag = m_firstParag->next(); + m_firstIndex = 0; + } + } + if ( parag == m_currentParag ) + { + operator++(); + emit currentParagraphDeleted(); + } +#ifdef DEBUG_ITERATOR + if ( m_currentParag ) + kdDebug(32500) << "KoTextIterator: firstParag:" << m_firstParag << " (" << m_firstParag->paragId() << ") - lastParag:" << m_lastParag << " (" << m_lastParag->paragId() << ") m_currentParag:" << m_currentParag << " (" << m_currentParag->paragId() << ")" << endl; +#endif +} + +// Go to next paragraph that we must iterate over +void KoTextIterator::operator++() +{ + if ( !m_currentParag ) { + kdDebug(32500) << k_funcinfo << " called past the end" << endl; + return; + } + if ( m_currentParag == m_lastParag ) { + m_currentParag = 0L; +#ifdef DEBUG_ITERATOR + kdDebug(32500) << "KoTextIterator++: done, after last parag " << m_lastParag << endl; +#endif + return; + } + bool forw = ! ( m_options & KFindDialog::FindBackwards ); + KoTextParag* parag = forw ? m_currentParag->next() : m_currentParag->prev(); + if ( parag ) + { + m_currentParag = parag; + } + else + { + nextTextObject(); + } +#ifdef DEBUG_ITERATOR + if ( m_currentParag ) + kdDebug(32500) << "KoTextIterator++ (" << *m_currentTextObj << "," << + m_currentParag->paragId() << ")" << endl; + else + kdDebug(32500) << "KoTextIterator++ (at end)" << endl; +#endif +} + +void KoTextIterator::nextTextObject() +{ + bool forw = ! ( m_options & KFindDialog::FindBackwards ); + do { + if ( forw ) { + ++m_currentTextObj; + if ( m_currentTextObj == m_lstObjects.end() ) + m_currentParag = 0L; // done + else + m_currentParag = (*m_currentTextObj)->textDocument()->firstParag(); + } else { + if ( m_currentTextObj == m_lstObjects.begin() ) + m_currentParag = 0L; // done + else + { + --m_currentTextObj; + m_currentParag = (*m_currentTextObj)->textDocument()->lastParag(); + } + } + } + // loop in case this new textobject is not visible + while ( m_currentParag && !(*m_currentTextObj)->isVisible() ); +#ifdef DEBUG_ITERATOR + if ( m_currentParag ) + kdDebug(32500) << k_funcinfo << " m_currentTextObj=" << (*m_currentTextObj) << endl; +#endif +} + +bool KoTextIterator::atEnd() const +{ + // operator++ sets m_currentParag to 0 when it's done + return m_currentParag == 0L; +} + +int KoTextIterator::currentStartIndex() const +{ + return currentTextAndIndex().first; +} + +QString KoTextIterator::currentText() const +{ + return currentTextAndIndex().second; +} + +QPair KoTextIterator::currentTextAndIndex() const +{ + Q_ASSERT( m_currentParag ); + Q_ASSERT( m_currentParag->string() ); + QString str = m_currentParag->string()->toString(); + str.truncate( str.length() - 1 ); // remove trailing space + bool forw = ! ( m_options & KFindDialog::FindBackwards ); + if ( m_currentParag == m_firstParag ) + { + if ( m_firstParag == m_lastParag ) // special case, needs truncating at both ends + return forw ? qMakePair( m_firstIndex, str.mid( m_firstIndex, m_lastIndex - m_firstIndex ) ) + : qMakePair( m_lastIndex, str.mid( m_lastIndex, m_firstIndex - m_lastIndex ) ); + else + return forw ? qMakePair( m_firstIndex, str.mid( m_firstIndex ) ) + : qMakePair( 0, str.left( m_firstIndex ) ); + } + if ( m_currentParag == m_lastParag ) + { + return forw ? qMakePair( 0, str.left( m_lastIndex ) ) + : qMakePair( m_lastIndex, str.mid( m_lastIndex ) ); + } + // Not the first parag, nor the last, so we return it all + return qMakePair( 0, str ); +} + +bool KoTextIterator::hasText() const +{ + // Same logic as currentTextAndIndex, but w/o calling it, to avoid all the string copying + bool forw = ! ( m_options & KFindDialog::FindBackwards ); + int strLength = m_currentParag->string()->length() - 1; + if ( m_currentParag == m_firstParag ) + { + if ( m_firstParag == m_lastParag ) + return m_firstIndex < m_lastIndex; + else + return forw ? m_firstIndex < strLength + : m_firstIndex > 0; + } + if ( m_currentParag == m_lastParag ) + return forw ? m_lastIndex > 0 + : m_lastIndex < strLength; + return strLength > 0; +} + +void KoTextIterator::setOptions( int options ) +{ + if ( m_options != options ) + { + bool wasBack = (m_options & KFindDialog::FindBackwards); + bool isBack = (options & KFindDialog::FindBackwards); + if ( wasBack != isBack ) + { + qSwap( m_firstParag, m_lastParag ); + qSwap( m_firstIndex, m_lastIndex ); + if ( m_currentParag == 0 ) // done? -> reinit + { +#ifdef DEBUG_ITERATOR + kdDebug(32500) << k_funcinfo << "was done -> reinit" << endl; +#endif + restart(); + } + } + bool wasFromCursor = (m_options & KFindDialog::FromCursor); + bool isFromCursor = (options & KFindDialog::FromCursor); + // We can only handle the case where fromcursor got removed. + // If it got added, then we need a textview to take the cursor position from... + if ( wasFromCursor && !isFromCursor ) + { + // We also can't handle the "selected text" option here + // It's very hard to have a cursor that's not at the beginning + // or end of the selection, anyway. + if ( ! (options & KFindDialog::SelectedText ) ) + { + // Set m_firstParag/m_firstIndex to the beginning of the first object + // (end of last object when going backwards) + KoTextParag* firstParag = m_lstObjects.first()->textDocument()->firstParag(); + int firstIndex = 0; + KoTextParag* lastParag = m_lstObjects.last()->textDocument()->lastParag(); + int lastIndex = lastParag->length()-1; + m_firstParag = (!isBack) ? firstParag : lastParag; + m_firstIndex = (!isBack) ? firstIndex : lastIndex; +#ifdef DEBUG_ITERATOR + kdDebug(32500) << "setOptions: FromCursor removed. New m_firstParag=" << m_firstParag << " (" << m_firstParag->paragId() << ") isBack=" << isBack << endl; +#endif + } + } + m_options = options; + } +} + +#include "KoTextIterator.moc" -- cgit v1.2.1