lib Library API Documentation

kotextiterator.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2002 David Faure <faure@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License version 2 as published by the Free Software Foundation.
00007 
00008    This library is distributed in the hope that it will be useful,
00009    but WITHOUT ANY WARRANTY; without even the implied warranty of
00010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011    Library General Public License for more details.
00012 
00013    You should have received a copy of the GNU Library General Public License
00014    along with this library; see the file COPYING.LIB.  If not, write to
00015    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00016    Boston, MA 02111-1307, USA.
00017 */
00018 
00019 #include "kotextiterator.h"
00020 #include "kotextview.h"
00021 #include <kfinddialog.h>
00022 #include <kdebug.h>
00023 #include <assert.h>
00024 
00025 //#define DEBUG_ITERATOR
00026 
00035 void KoTextIterator::init( const QValueList<KoTextObject *> & lstObjects, KoTextView* textView, int options )
00036 {
00037     Q_ASSERT( !lstObjects.isEmpty() );
00038 
00039     m_lstObjects.clear();
00040     m_firstParag = 0;
00041     m_firstIndex = 0;
00042     m_options = options;
00043 
00044     // 'From Cursor' option
00045     if ( options & KFindDialog::FromCursor )
00046     {
00047         if ( textView ) {
00048             m_firstParag = textView->cursor()->parag();
00049             m_firstIndex = textView->cursor()->index();
00050         } else {
00051             // !? FromCursor option can't work
00052             m_options &= ~KFindDialog::FromCursor;
00053             kdWarning(32500) << "FromCursor specified, but no textview?" << endl;
00054         }
00055     } // no else here !
00056 
00057     bool forw = ! ( options & KFindDialog::FindBackwards );
00058 
00059     // 'Selected Text' option
00060     if ( textView && ( options & KFindDialog::SelectedText ) )
00061     {
00062         KoTextObject* textObj = textView->textObject();
00063         KoTextCursor c1 = textObj->textDocument()->selectionStartCursor( KoTextDocument::Standard );
00064         KoTextCursor c2 = textObj->textDocument()->selectionEndCursor( KoTextDocument::Standard );
00065         if ( !m_firstParag ) // not from cursor
00066         {
00067             m_firstParag = forw ? c1.parag() : c2.parag();
00068             m_firstIndex = forw ? c1.index() : c2.index();
00069         }
00070         m_lastParag = forw ? c2.parag() : c1.parag();
00071         m_lastIndex = forw ? c2.index() : c1.index();
00072         // Find in the selection only -> only one textobject
00073         m_lstObjects.append( textObj );
00074         m_currentTextObj = m_lstObjects.begin();
00075     }
00076     else
00077     {
00078         // Not "selected text" -> loop through all textobjects
00079         m_lstObjects = lstObjects;
00080         if ( textView && (options & KFindDialog::FromCursor) )
00081         {
00082             KoTextObject* initialFirst = m_lstObjects.first();
00083             // textView->textObject() should be first in m_lstObjects (last when going backwards) !
00084             // Let's ensure this is the case, but without changing the order of the objects.
00085             if ( forw ) {
00086                 while( m_lstObjects.first() != textView->textObject() ) {
00087                     KoTextObject* textobj = m_lstObjects.front();
00088                     m_lstObjects.pop_front();
00089                     m_lstObjects.push_back( textobj );
00090                     if ( m_lstObjects.first() == initialFirst ) { // safety
00091                         kdWarning(32500) << "Didn't manage to find " << textView->textObject() << " in the list of textobjects!!!" << endl;
00092                         break;
00093                     }
00094                 }
00095             } else {
00096                 while( m_lstObjects.last() != textView->textObject() ) {
00097                     KoTextObject* textobj = m_lstObjects.back();
00098                     m_lstObjects.pop_back();
00099                     m_lstObjects.push_front( textobj );
00100                     if ( m_lstObjects.first() == initialFirst ) { // safety
00101                         kdWarning(32500) << "Didn't manage to find " << textView->textObject() << " in the list of textobjects!!!" << endl;
00102                         break;
00103                     }
00104                 }
00105             }
00106         }
00107 
00108         KoTextParag* firstParag = m_lstObjects.first()->textDocument()->firstParag();
00109         int firstIndex = 0;
00110         KoTextParag* lastParag = m_lstObjects.last()->textDocument()->lastParag();
00111         int lastIndex = lastParag->length()-1;
00112         if ( !m_firstParag ) // only set this when not 'from cursor'.
00113         {
00114             m_firstParag = forw ? firstParag : lastParag;
00115             m_firstIndex = forw ? firstIndex : lastIndex;
00116         }
00117         // always set the ending point
00118         m_lastParag = forw ? lastParag : firstParag;
00119         m_lastIndex = forw ? lastIndex : firstIndex;
00120         m_currentTextObj = forw ? m_lstObjects.begin() : m_lstObjects.fromLast();
00121     }
00122 
00123     assert( *m_currentTextObj ); // all branches set it
00124     assert( m_firstParag );
00125     assert( m_lastParag );
00126     Q_ASSERT( (*m_currentTextObj)->isVisible() );
00127     m_currentParag = m_firstParag;
00128 #ifdef DEBUG_ITERATOR
00129     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;
00130     QValueList<KoTextObject *>::Iterator it = m_lstObjects.begin();
00131     for( ; it != m_lstObjects.end(); ++it )
00132         kdDebug(32500) << (*it) << " " << (*it)->name() << endl;
00133 #endif
00134     Q_ASSERT( (*m_currentTextObj)->textDocument() == m_currentParag->textDocument() );
00135     Q_ASSERT( (forw?m_lstObjects.last():m_lstObjects.first())->textDocument() == m_lastParag->textDocument() );
00136 
00137     connectTextObjects();
00138 }
00139 
00140 void KoTextIterator::restart()
00141 {
00142     if( m_lstObjects.isEmpty() )
00143         return;
00144     m_currentParag = m_firstParag;
00145     bool forw = ! ( m_options & KFindDialog::FindBackwards );
00146     Q_ASSERT( ! (m_options & KFindDialog::FromCursor) ); // doesn't make much sense to keep it, right?
00147     if ( (m_options & KFindDialog::FromCursor) || forw )
00148         m_currentTextObj = m_lstObjects.begin();
00149     else
00150         m_currentTextObj = m_lstObjects.fromLast();
00151     if ( !(*m_currentTextObj)->isVisible() )
00152         nextTextObject();
00153 #ifdef DEBUG_ITERATOR
00154     if ( m_currentParag )
00155         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;
00156     else
00157         kdDebug(32500) << "KoTextIterator::restart - nowhere to go!" << endl;
00158 #endif
00159 }
00160 
00161 void KoTextIterator::connectTextObjects()
00162 {
00163     QValueList<KoTextObject *>::Iterator it = m_lstObjects.begin();
00164     for( ; it != m_lstObjects.end(); ++it ) {
00165         connect( (*it), SIGNAL( paragraphDeleted( KoTextParag* ) ),
00166                  this, SLOT( slotParagraphDeleted( KoTextParag* ) ) );
00167         connect( (*it), SIGNAL( paragraphModified( KoTextParag*, int, int, int ) ),
00168                  this, SLOT( slotParagraphModified( KoTextParag*, int, int, int ) ) );
00169         // We don't connect to destroyed(), because for undo/redo purposes,
00170         // we never really delete textdocuments nor textobjects.
00171         // So this is never called.
00172         // Instead the textobject is simply set to invisible, and this is handled by nextTextObject
00173     }
00174 }
00175 
00176 void KoTextIterator::slotParagraphModified( KoTextParag* parag, int modifyType, int pos, int length )
00177 {
00178     if ( parag == m_currentParag )
00179         emit currentParagraphModified( modifyType, pos, length );
00180 }
00181 
00182 void KoTextIterator::slotParagraphDeleted( KoTextParag* parag )
00183 {
00184 #ifdef DEBUG_ITERATOR
00185     kdDebug(32500) << "slotParagraphDeleted " << parag << endl;
00186 #endif
00187     // Note that the direction doesn't matter here. A begin/end
00188     // at end of parag N or at beginning of parag N+1 is the same,
00189     // and m_firstIndex/m_lastIndex becomes irrelevant, anyway.
00190     if ( parag == m_lastParag )
00191     {
00192         if ( m_lastParag->prev() ) {
00193             m_lastParag = m_lastParag->prev();
00194             m_lastIndex = m_lastParag->length()-1;
00195         } else {
00196             m_lastParag = m_lastParag->next();
00197             m_lastIndex = 0;
00198         }
00199     }
00200     if ( parag == m_firstParag )
00201     {
00202         if ( m_firstParag->prev() ) {
00203             m_firstParag = m_firstParag->prev();
00204             m_firstIndex = m_firstParag->length()-1;
00205         } else {
00206             m_firstParag = m_firstParag->next();
00207             m_firstIndex = 0;
00208         }
00209     }
00210     if ( parag == m_currentParag )
00211     {
00212         operator++();
00213     }
00214 #ifdef DEBUG_ITERATOR
00215     kdDebug(32500) << "firstParag:" << m_firstParag << " (" << m_firstParag->paragId() << ") -  lastParag:" << m_lastParag << " (" << m_lastParag->paragId() << ") m_currentParag:" << m_currentParag << " (" << m_currentParag->paragId() << ")" << endl;
00216 #endif
00217 }
00218 
00219 // Go to next paragraph that we must iterate over
00220 void KoTextIterator::operator++()
00221 {
00222     if ( !m_currentParag ) {
00223         kdDebug(32500) << k_funcinfo << " called past the end" << endl;
00224         return;
00225     }
00226     if ( m_currentParag == m_lastParag ) {
00227         m_currentParag = 0L;
00228 #ifdef DEBUG_ITERATOR
00229         kdDebug(32500) << "KoTextIterator++: done, after last parag " << m_lastParag << endl;
00230 #endif
00231         return;
00232     }
00233     bool forw = ! ( m_options & KFindDialog::FindBackwards );
00234     KoTextParag* parag = forw ? m_currentParag->next() : m_currentParag->prev();
00235     if ( parag )
00236     {
00237         m_currentParag = parag;
00238     }
00239     else
00240     {
00241         nextTextObject();
00242     }
00243 #ifdef DEBUG_ITERATOR
00244     kdDebug(32500) << "KoTextIterator++ (" << *m_currentTextObj << "," <<
00245         (m_currentParag ? m_currentParag->paragId() : 0) << ")" << endl;
00246 #endif
00247 }
00248 
00249 void KoTextIterator::nextTextObject()
00250 {
00251     bool forw = ! ( m_options & KFindDialog::FindBackwards );
00252     do {
00253         if ( forw ) {
00254             ++m_currentTextObj;
00255             if ( m_currentTextObj == m_lstObjects.end() )
00256                 m_currentParag = 0L; // done
00257             else
00258                 m_currentParag = (*m_currentTextObj)->textDocument()->firstParag();
00259         } else {
00260             if ( m_currentTextObj == m_lstObjects.begin() )
00261                 m_currentParag = 0L; // done
00262             else
00263             {
00264                 --m_currentTextObj;
00265                 m_currentParag = (*m_currentTextObj)->textDocument()->lastParag();
00266             }
00267         }
00268     }
00269     // loop in case this new textobject is not visible
00270     while ( m_currentParag && !(*m_currentTextObj)->isVisible() );
00271 #ifdef DEBUG_ITERATOR
00272     kdDebug(32500) << k_funcinfo << " m_currentTextObj=" << (*m_currentTextObj) << endl;
00273 #endif
00274 }
00275 
00276 bool KoTextIterator::atEnd() const
00277 {
00278     // operator++ sets m_currentParag to 0 when it's done
00279     return m_currentParag == 0L;
00280 }
00281 
00282 int KoTextIterator::currentStartIndex() const
00283 {
00284     return currentTextAndIndex().first;
00285 }
00286 
00287 QString KoTextIterator::currentText() const
00288 {
00289     return currentTextAndIndex().second;
00290 }
00291 
00292 QPair<int, QString> KoTextIterator::currentTextAndIndex() const
00293 {
00294     Q_ASSERT( m_currentParag );
00295     Q_ASSERT( m_currentParag->string() );
00296     QString str = m_currentParag->string()->toString();
00297     str.truncate( str.length() - 1 ); // remove trailing space
00298     bool forw = ! ( m_options & KFindDialog::FindBackwards );
00299     if ( m_currentParag == m_firstParag )
00300     {
00301         if ( m_firstParag == m_lastParag ) // special case, needs truncating at both ends
00302             return forw ? qMakePair( m_firstIndex, str.mid( m_firstIndex, m_lastIndex - m_firstIndex ) )
00303                 : qMakePair( m_lastIndex, str.mid( m_lastIndex, m_firstIndex - m_lastIndex ) );
00304         else
00305             return forw ? qMakePair( m_firstIndex, str.mid( m_firstIndex ) )
00306                         : qMakePair( 0, str.left( m_firstIndex ) );
00307     }
00308     if ( m_currentParag == m_lastParag )
00309     {
00310         return forw ? qMakePair( 0, str.left( m_lastIndex ) )
00311                     : qMakePair( m_lastIndex, str.mid( m_lastIndex ) );
00312     }
00313     // Not the first parag, nor the last, so we return it all
00314     return qMakePair( 0, str );
00315 }
00316 
00317 bool KoTextIterator::hasText() const
00318 {
00319     // Same logic as currentTextAndIndex, but w/o calling it, to avoid all the string copying
00320     bool forw = ! ( m_options & KFindDialog::FindBackwards );
00321     int strLength = m_currentParag->string()->length() - 1;
00322     if ( m_currentParag == m_firstParag )
00323     {
00324         if ( m_firstParag == m_lastParag )
00325             return m_firstIndex < m_lastIndex;
00326         else
00327             return forw ? m_firstIndex < strLength
00328                         : m_firstIndex > 0;
00329     }
00330     if ( m_currentParag == m_lastParag )
00331         return forw ? m_lastIndex > 0
00332                     : m_lastIndex < strLength;
00333     return strLength > 0;
00334 }
00335 
00336 void KoTextIterator::setOptions( int options )
00337 {
00338     if ( m_options != options )
00339     {
00340         bool wasBack = (m_options & KFindDialog::FindBackwards);
00341         bool isBack = (options & KFindDialog::FindBackwards);
00342         if ( wasBack != isBack )
00343         {
00344             qSwap( m_firstParag, m_lastParag );
00345             qSwap( m_firstIndex, m_lastIndex );
00346             if ( m_currentParag == 0 ) // done? -> reinit
00347             {
00348 #ifdef DEBUG_ITERATOR
00349                 kdDebug(32500) << k_funcinfo << "was done -> reinit" << endl;
00350 #endif
00351                 restart();
00352             }
00353         }
00354         bool wasFromCursor = (m_options & KFindDialog::FromCursor);
00355         bool isFromCursor = (options & KFindDialog::FromCursor);
00356         // We can only handle the case where fromcursor got removed.
00357         // If it got added, then we need a textview to take the cursor position from...
00358         if ( wasFromCursor && !isFromCursor )
00359         {
00360             // We also can't handle the "selected text" option here
00361             // It's very hard to have a cursor that's not at the beginning
00362             // or end of the selection, anyway.
00363             if ( ! (options & KFindDialog::SelectedText ) )
00364             {
00365                 // Set m_firstParag/m_firstIndex to the beginning of the first object
00366                 // (end of last object when going backwards)
00367                 KoTextParag* firstParag = m_lstObjects.first()->textDocument()->firstParag();
00368                 int firstIndex = 0;
00369                 KoTextParag* lastParag = m_lstObjects.last()->textDocument()->lastParag();
00370                 int lastIndex = lastParag->length()-1;
00371                 m_firstParag = (!isBack) ? firstParag : lastParag;
00372                 m_firstIndex = (!isBack) ? firstIndex : lastIndex;
00373 #ifdef DEBUG_ITERATOR
00374                 kdDebug(32500) << "setOptions: FromCursor removed. New m_firstParag=" << m_firstParag << " (" << m_firstParag->paragId() << ") isBack=" << isBack << endl;
00375 #endif
00376             }
00377         }
00378         m_options = options;
00379     }
00380 }
00381 
00382 #include "kotextiterator.moc"
KDE Logo
This file is part of the documentation for lib Library Version 1.3.5.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Nov 17 13:19:26 2004 by doxygen 1.3.5 written by Dimitri van Heesch, © 1997-2003