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 06:54:18 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003