F:/KPlato/koffice/libs/kotext/KoTextIterator.cpp

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

Généré le Wed Nov 22 23:41:09 2006 pour KPlato par  doxygen 1.5.1-p1