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