summaryrefslogtreecommitdiffstats
path: root/lib/kotext/KoTextIterator.cpp
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 01:29:50 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 01:29:50 +0000
commit8362bf63dea22bbf6736609b0f49c152f975eb63 (patch)
tree0eea3928e39e50fae91d4e68b21b1e6cbae25604 /lib/kotext/KoTextIterator.cpp
downloadkoffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz
koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip
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
Diffstat (limited to 'lib/kotext/KoTextIterator.cpp')
-rw-r--r--lib/kotext/KoTextIterator.cpp389
1 files changed, 389 insertions, 0 deletions
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 <faure@kde.org>
+
+ 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 <kfinddialog.h>
+#include <kdebug.h>
+#include <assert.h>
+
+//#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<KoTextObject *> & 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<KoTextObject *>::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<KoTextObject *>::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<int, QString> 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"