• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KTextEditor

smartrange.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2003-2005 Hamish Rodda <rodda@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 "smartrange.h"
00020 
00021 #include <QtCore/QStack>
00022 
00023 #include "document.h"
00024 #include "view.h"
00025 #include "attribute.h"
00026 #include "rangefeedback.h"
00027 
00028 #include <kaction.h>
00029 #include <kdebug.h>
00030 
00031 using namespace KTextEditor;
00032 
00033 SmartRange::SmartRange(SmartCursor* start, SmartCursor* end, SmartRange * parent, InsertBehaviors insertBehavior )
00034   : Range(start, end)
00035   , m_attribute(0L)
00036   , m_parentRange(parent)
00037   , m_ownsAttribute(false)
00038 {
00039   setInsertBehavior(insertBehavior);
00040 
00041   // Not calling setParentRange here...:
00042   // 1) subclasses are not yet constructed
00043   // 2) it would otherwise give the wrong impression
00044   if (m_parentRange)
00045     m_parentRange->insertChildRange(this);
00046 }
00047 
00048 SmartRange::~SmartRange( )
00049 {
00050   deleteChildRanges();
00051 
00052   setParentRange(0L);
00053 
00054   /*if (!m_deleteCursors)
00055   {
00056     // Save from deletion in the parent
00057     m_start = 0L;
00058     m_end = 0L;
00059   }*/
00060 }
00061 
00062 bool SmartRange::confineToRange(const Range& range)
00063 {
00064   if (!Range::confineToRange(range))
00065     // Don't need to check if children should be confined, they already are
00066     return false;
00067 
00068   foreach (SmartRange* child, m_childRanges)
00069     child->confineToRange(*this);
00070 
00071   return true;
00072 }
00073 
00074 bool SmartRange::expandToRange(const Range& range)
00075 {
00076   if (!Range::expandToRange(range))
00077     // Don't need to check if parents should be expanded, they already are
00078     return false;
00079 
00080   if (parentRange())
00081     parentRange()->expandToRange(*this);
00082 
00083   return true;
00084 }
00085 
00086 void SmartRange::setRange(const Range& range)
00087 {
00088   if (range == *this)
00089     return;
00090 
00091   Range old = *this;
00092 
00093   Range::setRange(range);
00094 
00095   rangeChanged(0L, old);
00096 }
00097 
00098 const QList<SmartRange*>& SmartRange::childRanges() const
00099 {
00100   return m_childRanges;
00101 }
00102 
00103 SmartRange * SmartRange::childBefore( const SmartRange * range ) const
00104 {
00105   int index = m_childRanges.indexOf(const_cast<SmartRange*>(range));
00106   if (--index >= 0)
00107     return m_childRanges[index];
00108   return 0L;
00109 }
00110 
00111 SmartRange * SmartRange::childAfter( const SmartRange * range ) const
00112 {
00113   int index = m_childRanges.indexOf(const_cast<SmartRange*>(range));
00114   if (index != -1 && ++index < m_childRanges.count())
00115     return m_childRanges[index];
00116   return 0L;
00117 }
00118 
00119 void SmartRange::insertChildRange( SmartRange * newChild )
00120 {
00121   // This function is backwards because it's most likely the new child will go onto the end
00122   // of the child list
00123   Q_ASSERT(newChild->parentRange() == this);
00124 
00125   // A new child has been added, so expand this range if required.
00126   expandToRange(*newChild);
00127 
00128   QMutableListIterator<SmartRange*> it = m_childRanges;
00129   it.toBack();
00130 
00131   bool done = false;
00132   while (it.hasPrevious()) {
00133     if (it.peekPrevious()->end() <= newChild->start()) {
00134       it.insert(newChild);
00135       if (it.hasNext() && it.peekNext()->start() < newChild->end())
00136       {
00137           Range oldRange = *it.peekNext();
00138           //Give a warning here, because this most probably results in unwanted behavior, and is extremely hard to debug. This at least gives a clue what'is going wrong. The alternative would be an assertion.
00139           it.peekNext()->start() = newChild->end();
00140           
00141           kDebug() << "SmartRange warning: " << this << ": Added child-range " << newChild << "(" << *newChild << ") intersects child-range " << it.peekNext() << "(" << oldRange << "), the second one is trimmed to " << *it.peekNext() << endl;
00142       }
00143 
00144       done = true;
00145       break;
00146     }
00147 
00148     it.previous();
00149   }
00150 
00151   if (!done) {
00152     if (m_childRanges.count() && m_childRanges.first()->start() < newChild->end())
00153     {
00154         Range oldRange = *m_childRanges.first();
00155         
00156         m_childRanges.first()->start() = newChild->end();
00157         
00158         kDebug() << "SmartRange warning: " << this << ": Added child-range " << newChild << "(" << *newChild << ") intersects child-range " << m_childRanges.first() << "(" << oldRange << "), the second one is trimmed to " << *m_childRanges.first() << endl;
00159     }
00160     m_childRanges.prepend(newChild);
00161   }
00162 
00163   foreach (SmartRangeNotifier* n, m_notifiers)
00164     emit n->childRangeInserted(this, newChild);
00165 
00166   foreach (SmartRangeWatcher* w, m_watchers)
00167     w->childRangeInserted(this, newChild);
00168 }
00169 
00170 void SmartRange::removeChildRange(SmartRange* child)
00171 {
00172   int index = m_childRanges.lastIndexOf(child);
00173   if (index != -1) {
00174     m_childRanges.removeAt(index);
00175 
00176     foreach (SmartRangeNotifier* n, m_notifiers)
00177       emit n->childRangeInserted(this, child);
00178 
00179     foreach (SmartRangeWatcher* w, m_watchers)
00180       w->childRangeInserted(this, child);
00181   }
00182 }
00183 
00184 SmartRange * SmartRange::mostSpecificRange( const Range & input ) const
00185 {
00186   if (!input.isValid())
00187     return 0L;
00188 
00189   if (contains(input)) {
00190     foreach (SmartRange* r, m_childRanges)
00191       if (r->contains(input))
00192         return r->mostSpecificRange(input);
00193 
00194     return const_cast<SmartRange*>(this);
00195 
00196   } else if (parentRange()) {
00197     return parentRange()->mostSpecificRange(input);
00198 
00199   } else {
00200     return 0L;
00201   }
00202 }
00203 
00204 SmartRange * SmartRange::firstRangeContaining( const Cursor & pos ) const
00205 {
00206   if (!pos.isValid())
00207     return 0L;
00208 
00209   if (contains(pos)) {
00210     if (parentRange() && parentRange()->contains(pos))
00211       return parentRange()->firstRangeContaining(pos);
00212 
00213     return const_cast<SmartRange*>(this);
00214 
00215   } else {
00216     if (!parentRange())
00217       return 0L;
00218 
00219     return parentRange()->firstRangeContaining(pos);
00220   }
00221 }
00222 
00223 SmartRange * SmartRange::deepestRangeContaining( const Cursor & pos, QStack<SmartRange*>* rangesEntered, QStack<SmartRange*>* rangesExited ) const
00224 {
00225   if (!pos.isValid()) {
00226     // Just leave all ranges
00227     if (rangesExited) {
00228       SmartRange* range = const_cast<SmartRange*>(this);
00229       while (range) {
00230         rangesExited->append(range);
00231         range = range->parentRange();
00232       }
00233     }
00234     return 0L;
00235   }
00236 
00237   return deepestRangeContainingInternal(pos, rangesEntered, rangesExited, true);
00238 }
00239 
00240 SmartRange * SmartRange::deepestRangeContainingInternal( const Cursor & pos, QStack<SmartRange*>* rangesEntered, QStack<SmartRange*>* rangesExited, bool first ) const
00241 {
00242   if (contains(pos)) {
00243     if (!first && rangesEntered)
00244       rangesEntered->push(const_cast<SmartRange*>(this));
00245 
00246     foreach (SmartRange* r, m_childRanges) {
00247       int result = r->positionRelativeToCursor(pos);
00248       if (result == 0)
00249         return r->deepestRangeContainingInternal(pos, rangesEntered, rangesExited);
00250       else if (result == 1)
00251         break;
00252     }
00253 
00254     return const_cast<SmartRange*>(this);
00255 
00256   } else {
00257     if (rangesExited)
00258       rangesExited->push(const_cast<SmartRange*>(this));
00259 
00260     if (!parentRange())
00261       return 0L;
00262 
00263     // first is true, because the parentRange won't be "entered" on first descent
00264     return parentRange()->deepestRangeContainingInternal(pos, rangesEntered, rangesExited, true);
00265   }
00266 }
00267 
00268 Document* SmartRange::document( ) const
00269 {
00270   return smartStart().document();
00271 }
00272 
00273 void SmartRange::associateAction( KAction * action )
00274 {
00275   m_associatedActions.append(action);
00276 
00277   bool enable = false;
00278   if (View* v = document()->activeView())
00279     if (contains(v->cursorPosition()))
00280       enable = true;
00281 
00282   action->setEnabled(enable);
00283 
00284   if (m_associatedActions.count() == 1)
00285     checkFeedback();
00286 }
00287 
00288 void SmartRange::dissociateAction( KAction * action )
00289 {
00290   m_associatedActions.removeAll(action);
00291   if (!m_associatedActions.count())
00292     checkFeedback();
00293 }
00294 
00295 void SmartRange::clearAssociatedActions( )
00296 {
00297   m_associatedActions.clear();
00298   checkFeedback();
00299 }
00300 
00301 SmartRange::InsertBehaviors SmartRange::insertBehavior( ) const
00302 {
00303   return ((smartStart().insertBehavior() == SmartCursor::MoveOnInsert) ? DoNotExpand : ExpandLeft) | ((smartEnd().insertBehavior() == SmartCursor::MoveOnInsert) ? ExpandRight : DoNotExpand);
00304 }
00305 
00306 void SmartRange::setInsertBehavior(SmartRange::InsertBehaviors behavior)
00307 {
00308   static_cast<SmartCursor*>(m_start)->setInsertBehavior((behavior & ExpandLeft) ? SmartCursor::StayOnInsert : SmartCursor::MoveOnInsert);
00309   static_cast<SmartCursor*>(m_end)->setInsertBehavior((behavior & ExpandRight) ? SmartCursor::MoveOnInsert : SmartCursor::StayOnInsert);
00310 }
00311 
00312 void SmartRange::clearChildRanges()
00313 {
00314   foreach (SmartRange* r, m_childRanges)
00315     r->removeText();
00316 }
00317 
00318 void SmartRange::deleteChildRanges()
00319 {
00320   // FIXME: Probably more efficient to prevent them from unlinking themselves?
00321   qDeleteAll(m_childRanges);
00322 
00323   // i.e. this is probably already clear
00324   m_childRanges.clear();
00325 }
00326 
00327 void SmartRange::clearAndDeleteChildRanges( )
00328 {
00329   // FIXME: Probably more efficient to prevent them from unlinking themselves?
00330   foreach (SmartRange* r, m_childRanges)
00331     r->removeText();
00332 
00333   qDeleteAll(m_childRanges);
00334 
00335   // i.e. this is probably already clear
00336   m_childRanges.clear();
00337 }
00338 
00339 void SmartRange::setParentRange( SmartRange * r )
00340 {
00341   if (m_parentRange == r)
00342     return;
00343 
00344   if (m_parentRange)
00345     m_parentRange->removeChildRange(this);
00346 
00347   SmartRange* oldParent = m_parentRange;
00348 
00349   m_parentRange = r;
00350 
00351   if (m_parentRange)
00352     m_parentRange->insertChildRange(this);
00353 
00354   foreach (SmartRangeNotifier* n, m_notifiers)
00355     emit n->parentRangeChanged(this, m_parentRange, oldParent);
00356 
00357   foreach (SmartRangeWatcher* w, m_watchers)
00358     w->parentRangeChanged(this, m_parentRange, oldParent);
00359 }
00360 
00361 void SmartRange::setAttribute( Attribute::Ptr attribute )
00362 {
00363   if (attribute == m_attribute)
00364     return;
00365 
00366   Attribute::Ptr prev = m_attribute;
00367 
00368   m_attribute = attribute;
00369 
00370   foreach (SmartRangeNotifier* n, m_notifiers)
00371     emit n->rangeAttributeChanged(this, attribute, prev);
00372 
00373   foreach (SmartRangeWatcher* w, m_watchers)
00374     w->rangeAttributeChanged(this, attribute, prev);
00375 }
00376 
00377 Attribute::Ptr SmartRange::attribute( ) const
00378 {
00379   return m_attribute;
00380 }
00381 
00382 QStringList SmartRange::text( bool block ) const
00383 {
00384   return document()->textLines(*this, block);
00385 }
00386 
00387 bool SmartRange::replaceText( const QStringList & text, bool block )
00388 {
00389   return document()->replaceText(*this, text, block);
00390 }
00391 
00392 bool SmartRange::removeText( bool block )
00393 {
00394   return document()->removeText(*this, block);
00395 }
00396 
00397 void SmartRange::rangeChanged( Cursor* c, const Range& from )
00398 {
00399   Range::rangeChanged(c, from);
00400 
00401   // Decide whether the parent range has expanded or contracted, if there is one
00402   if (parentRange() && (start() < from.start() || end() > from.end()))
00403     parentRange()->expandToRange(*this);
00404 
00405   // Adjust sibling ranges if required
00406   if (parentRange()) {
00407     if (SmartRange* beforeRange = parentRange()->childBefore(this)) {
00408       if (beforeRange->end() > start())
00409         beforeRange->end() = start();
00410     }
00411 
00412     if (SmartRange* afterRange = parentRange()->childAfter(this)) {
00413       if (afterRange->start() < end())
00414         afterRange->start() = end();
00415     }
00416   }
00417 
00418   // Contract child ranges if required
00419   if (childRanges().count()) {
00420     SmartRange* r;
00421     QList<SmartRange*>::ConstIterator it;
00422 
00423     int i = 0;
00424     if (start() > from.start()) {
00425       // Start has contracted - adjust from the start of the child ranges
00426       for (; i < childRanges().count(); ++i) {
00427         r = childRanges().at(i);
00428         if (r->start() < start())
00429           r->confineToRange(*this);
00430         else
00431           break;
00432       }
00433     }
00434 
00435     if (end() < from.end()) {
00436       // end has contracted - adjust from the start of the child ranges, if they
00437       // haven't already been adjusted above
00438       for (int j = childRanges().count() - 1; j >= i; --j) {
00439         r = childRanges().at(j);
00440         if (r->end() > end())
00441           r->confineToRange(*this);
00442         else
00443           break;
00444       }
00445     }
00446   }
00447 
00448   // SmartCursor and its subclasses take care of adjusting ranges if the tree
00449   // structure is being used.
00450   foreach (SmartRangeNotifier* n, m_notifiers)
00451     if (n->wantsDirectChanges()) {
00452       emit n->rangePositionChanged(this);
00453       emit n->rangeContentsChanged(this);
00454 
00455       if (start() == end())
00456         emit n->rangeEliminated(this);
00457     }
00458 
00459   foreach (SmartRangeWatcher* w, m_watchers)
00460     if (w->wantsDirectChanges()) {
00461       w->rangePositionChanged(this);
00462       w->rangeContentsChanged(this);
00463 
00464       if (start() == end())
00465         w->rangeEliminated(this);
00466     }
00467 }
00468 
00469 bool SmartRange::isSmartRange( ) const
00470 {
00471   return true;
00472 }
00473 
00474 SmartRange* SmartRange::toSmartRange( ) const
00475 {
00476   return const_cast<SmartRange*>(this);
00477 }
00478 
00479 bool SmartRange::hasParent( SmartRange * parent ) const
00480 {
00481   if (parentRange() == parent)
00482     return true;
00483 
00484   if (parentRange())
00485     return parentRange()->hasParent(parent);
00486 
00487   return false;
00488 }
00489 
00490 const QList< SmartRangeWatcher * > & SmartRange::watchers( ) const
00491 {
00492   return m_watchers;
00493 }
00494 
00495 void SmartRange::addWatcher( SmartRangeWatcher * watcher )
00496 {
00497   if (!m_watchers.contains(watcher))
00498     m_watchers.append(watcher);
00499 
00500   checkFeedback();
00501 }
00502 
00503 void SmartRange::removeWatcher( SmartRangeWatcher * watcher )
00504 {
00505   m_watchers.removeAll(watcher);
00506   checkFeedback();
00507 }
00508 
00509 SmartRangeNotifier * SmartRange::primaryNotifier( )
00510 {
00511   if (m_notifiers.isEmpty())
00512     m_notifiers.append(createNotifier());
00513 
00514   return m_notifiers.first();
00515 }
00516 
00517 const QList< SmartRangeNotifier * > SmartRange::notifiers( ) const
00518 {
00519   return m_notifiers;
00520 }
00521 
00522 void SmartRange::addNotifier( SmartRangeNotifier * notifier )
00523 {
00524   if (!m_notifiers.contains(notifier))
00525     m_notifiers.append(notifier);
00526 
00527   checkFeedback();
00528 }
00529 
00530 void SmartRange::removeNotifier( SmartRangeNotifier * notifier )
00531 {
00532   m_notifiers.removeAll(notifier);
00533   checkFeedback();
00534 }
00535 
00536 void SmartRange::deletePrimaryNotifier( )
00537 {
00538   if (m_notifiers.isEmpty())
00539     return;
00540 
00541   SmartRangeNotifier* n = m_notifiers.first();
00542   removeNotifier(n);
00543   delete n;
00544 }
00545 
00546 void SmartRange::checkFeedback( )
00547 {
00548 }
00549 
00550 // kate: space-indent on; indent-width 2; replace-tabs on;

KTextEditor

Skip menu "KTextEditor"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal