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

Kate

katesmartcursor.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2003 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 "katesmartcursor.h"
00020 
00021 #include "katedocument.h"
00022 #include "katesmartmanager.h"
00023 #include "katesmartcursornotifier.h"
00024 #include "katesmartrange.h"
00025 
00026 #include <kdebug.h>
00027 
00028 //#define DEBUG_KATESMARTCURSOR
00029 
00030 KateSmartCursor::KateSmartCursor(const KTextEditor::Cursor& position, KTextEditor::Document* doc, KTextEditor::SmartCursor::InsertBehavior insertBehavior)
00031   : KTextEditor::SmartCursor(position, doc, insertBehavior)
00032   , m_oldGroupLineStart(-1)
00033   , m_lastPosition(position)
00034   , m_feedbackEnabled(false)
00035   , m_isInternal(false)
00036   , m_lastPositionNeeded(false)
00037   , m_bypassTranslation(0)
00038   , m_notifier(0L)
00039   , m_watcher(0L)
00040 {
00041   if (position.line() > kateDocument()->lastLine()) {
00042     kWarning() << "Attempted to set cursor position " << position << " past end of document " << doc->documentRange();
00043     m_line = -1;
00044     m_column = -1;
00045   }
00046 
00047   // Replace straight line number with smartgroup + line offset
00048   m_smartGroup = kateDocument()->smartManager()->groupForLine(m_line);
00049   m_line = m_line - m_smartGroup->startLine();
00050   m_smartGroup->joined(this);
00051 
00052 #ifdef DEBUG_KATESMARTCURSOR
00053   kDebug() << "Cursor created at " << *this;
00054 #endif
00055 }
00056 
00057 KateSmartCursor::KateSmartCursor( KTextEditor::Document * doc, KTextEditor::SmartCursor::InsertBehavior insertBehavior )
00058   : KTextEditor::SmartCursor(KTextEditor::Cursor(), doc, insertBehavior)
00059   , m_oldGroupLineStart(-1)
00060   , m_feedbackEnabled(false)
00061   , m_isInternal(false)
00062   , m_lastPositionNeeded(false)
00063   , m_bypassTranslation(0)
00064   , m_notifier(0L)
00065   , m_watcher(0L)
00066 {
00067   // Replace straight line number with smartgroup + line offset
00068   m_smartGroup = kateDocument()->smartManager()->groupForLine(m_line);
00069   m_line = m_line - m_smartGroup->startLine();
00070   m_smartGroup->joined(this);
00071 
00072 #ifdef DEBUG_KATESMARTCURSOR
00073   kDebug() << this << "Cursor created at " << *this;
00074 #endif
00075 }
00076 
00077 KateSmartCursor::~KateSmartCursor()
00078 {
00079   if (m_notifier) {
00080     emit m_notifier->deleted(this);
00081     delete m_notifier;
00082   }
00083 
00084   if (m_watcher)
00085     m_watcher->deleted(this);
00086 
00087   if (!kateDocument()->smartManager()->isClearing())
00088     m_smartGroup->leaving(this);
00089 }
00090 
00091 KateSmartCursor::operator QString()
00092 {
00093   return QString("[%1,%1]").arg(line()).arg(column());
00094 }
00095 
00096 KateDocument* KateSmartCursor::kateDocument() const
00097 {
00098   return static_cast<KateDocument*>(document());
00099 }
00100 
00101 bool KateSmartCursor::isValid( ) const
00102 {
00103   return line() >= 0 && column() >= 0 && line() <= kateDocument()->lastLine() && column() <= kateDocument()->lineLength(line());
00104 }
00105 
00106 bool KateSmartCursor::isValid(const Cursor& position) const
00107 {
00108   return position.line() >= 0 && position.line() <= kateDocument()->lastLine() && position.column() >= 0 && position.column() <= kateDocument()->lineLength(position.line());
00109 }
00110 
00111 bool KateSmartCursor::atEndOfLine( ) const
00112 {
00113   return line() >= 0 && line() <= kateDocument()->lastLine() && column() >= kateDocument()->lineLength(line());
00114 }
00115 
00116 void KateSmartCursor::checkFeedback()
00117 {
00118   bool feedbackNeeded = m_watcher || m_notifier;
00119 
00120   m_lastPositionNeeded = feedbackNeeded || (range() && static_cast<KateSmartRange*>(range())->feedbackEnabled());
00121 
00122   if (m_feedbackEnabled != feedbackNeeded) {
00123     m_smartGroup->changeCursorFeedback(this);
00124     m_feedbackEnabled = feedbackNeeded;
00125   }
00126 }
00127 
00128 int KateSmartCursor::line( ) const
00129 {
00130   return m_smartGroup->startLine() + m_line;
00131 }
00132 
00133 void KateSmartCursor::setLine( int _line )
00134 {
00135   setPositionInternal(KTextEditor::Cursor(_line, m_column), false);
00136 }
00137 
00138 void KateSmartCursor::setPositionInternal( const KTextEditor::Cursor & pos, bool internal )
00139 {
00140   // Shortcut if there's no change :)
00141   if (*this == pos)
00142     return;
00143   
00144   //Previous position of the "other" side in the range that this cursor is part of
00145   KTextEditor::Cursor oldOther;
00146   
00147   if(m_range) {
00148     KTextEditor::SmartRange* smartRange = m_range->toSmartRange();
00149     
00150     if(!internal && smartRange) {
00151       KateSmartCursor& start = dynamic_cast<KateSmartCursor&>(smartRange->smartStart());
00152       KateSmartCursor& end = dynamic_cast<KateSmartCursor&>(smartRange->smartEnd());
00153       //Eventually move the other cursor first, so the smart-range cannot temporarily become an invalid range with start > end.
00154       //If we let it become invalid, that will create serious consistency problems in places that depend on it, like for example
00155       //the SmartRange::rangeChanged function.
00156       if(this == &start) {
00157         oldOther = end;
00158         if(pos > end)
00159           //We do the change with "internal" set, so the parent-range is not confined to the not yet transformed changed range
00160           end.setPositionInternal(pos, true);
00161       }else{
00162         Q_ASSERT(this == &end);
00163         oldOther = start;
00164         if(pos < start)
00165           //We do the change with "internal" set, so the parent-range is not confined to the not yet transformed changed range
00166           start.setPositionInternal(pos, true);
00167       }
00168     }
00169   }
00170 
00171   KTextEditor::Cursor old = *this;
00172 
00173   // Remember this position if the feedback system needs it
00174   if (m_lastPositionNeeded)
00175     m_lastPosition = *this;
00176 
00177   // Deal with crossing a smart group border
00178   bool haveToChangeGroups = !m_smartGroup->containsLine(pos.line());
00179   if (haveToChangeGroups) {
00180     m_smartGroup->leaving(this);
00181     m_smartGroup = kateDocument()->smartManager()->groupForLine(pos.line());
00182   }
00183 
00184   // Set the new position
00185   m_line = pos.line() - m_smartGroup->newStartLine();
00186   m_column = pos.column();
00187 
00188   // Finish dealing with crossing a smart group border
00189   if (haveToChangeGroups) {
00190     m_smartGroup->joined(this);
00191   }
00192 
00193   // Forget this position change if the feedback system doesn't need it
00194   if (!m_lastPositionNeeded)
00195     m_lastPosition = *this;
00196 
00197   // Adjustments only needed for non-internal position changes...
00198   if (!internal) {
00199     //We notify about the range as a whole, instead of using cursorChangedDirectory. This allows us
00200     //notifying the change to the other side too, if we did one.
00201     // Tell the range about this
00202     if (m_range) {
00203       if (this == &m_range->start())
00204         static_cast<KateSmartRange*>(m_range)->rangeChanged(0, KTextEditor::Range(old, oldOther));
00205       else
00206         static_cast<KateSmartRange*>(m_range)->rangeChanged(0, KTextEditor::Range(oldOther, old));
00207     }
00208   }
00209 
00210 #ifdef DEBUG_KATESMARTCURSOR
00211   kDebug() << this << "Cursor moved from" << old << "to" << *this;
00212 #endif
00213 }
00214 
00215 KTextEditor::SmartCursorNotifier* KateSmartCursor::notifier( )
00216 {
00217   if (!m_notifier) {
00218     m_notifier = new KateSmartCursorNotifier();
00219     checkFeedback();
00220   }
00221   return m_notifier;
00222 }
00223 
00224 void KateSmartCursor::deleteNotifier( )
00225 {
00226   delete m_notifier;
00227   m_notifier = 0L;
00228   checkFeedback();
00229 }
00230 
00231 void KateSmartCursor::setWatcher( KTextEditor::SmartCursorWatcher * watcher )
00232 {
00233   m_watcher = watcher;
00234   checkFeedback();
00235 }
00236 
00237 bool KateSmartCursor::translate( const KateEditInfo & edit )
00238 {
00239 #ifdef DEBUG_KATESMARTCURSOR
00240   kDebug() << this << "Translating cursor" << *this << "from " << edit.oldRange() << "to" << edit.newRange() << edit.editSource() << &edit;
00241 #endif
00242 
00243   if (m_bypassTranslation) {
00244     if (m_bypassTranslation == &edit) {
00245       // This cursor has already been moved for this edit
00246       m_bypassTranslation = 0;
00247       return true;
00248     }
00249 
00250     m_bypassTranslation = 0;
00251   }
00252 
00253   // If this cursor is before the edit, no action is required
00254   if (*this < edit.start())
00255     return false;
00256 
00257   // Calculate the new position
00258   KTextEditor::Cursor newPos;
00259 
00260   // If this cursor is on a line affected by the edit
00261   if (edit.oldRange().overlapsLine(line())) {
00262     // If this cursor is at the start of the edit
00263     if (*this == edit.start()) {
00264       // And it doesn't need to move, no action is required
00265       if (insertBehavior() == KTextEditor::SmartCursor::StayOnInsert)
00266         return false;
00267     }
00268 
00269     if (edit.oldRange().contains(*this)) {
00270       if (insertBehavior() == KTextEditor::SmartCursor::MoveOnInsert)
00271         newPos = edit.newRange().end();
00272       else
00273         newPos = edit.start();
00274 
00275     } else {
00276       newPos = *this + edit.translate();
00277     }
00278 
00279   } else {
00280     // just need to adjust line number
00281     newPos.setPosition(line() + edit.translate().line(), column());
00282   }
00283 
00284   if (newPos != *this) {
00285     // Catch corner case where the range is non-expanding, is zero length, and then the
00286     // start cursor would otherwise be placed before the end cursor.
00287     if (KTextEditor::SmartRange* range = smartRange()) {
00288       
00289       if (&(range->smartStart()) == this) {
00290         if (*this == edit.start()) {
00291           if (range->insertBehavior() == KTextEditor::SmartRange::DoNotExpand) {
00292             if (range->end() == *this) {
00293               KateSmartCursor* end = static_cast<KateSmartCursor*>(&(range->smartEnd()));
00294               end->setPositionInternal(newPos);
00295               // Don't let the end cursor get translated again
00296               end->m_bypassTranslation = &edit;
00297             }
00298           }
00299         }
00300       }else{
00301       }
00302     }
00303 
00304     setPositionInternal(newPos);
00305     return true;
00306   }
00307 
00308   return false;
00309 }
00310 
00311 bool KateSmartCursor::cursorMoved( ) const
00312 {
00313   bool ret = m_oldGroupLineStart != m_smartGroup->startLine();
00314   m_oldGroupLineStart = m_smartGroup->startLine();
00315   return ret;
00316 }
00317 
00318 void KateSmartCursor::setLineInternal( int newLine, bool internal )
00319 {
00320   setPositionInternal(KTextEditor::Cursor(newLine, column()), internal);
00321 }
00322 
00323 void KateSmartCursor::translated(const KateEditInfo & edit)
00324 {
00325   if (*this < edit.start()) {
00326     if (!range() || !static_cast<KateSmartRange*>(range())->feedbackEnabled())
00327       m_lastPosition = *this;
00328     return;
00329   }
00330 
00331   // We can rely on m_lastPosition because it is updated in translate(), otherwise just shifted() is called
00332   if (m_lastPosition != *this) {
00333     // position changed
00334     if (m_notifier)
00335       emit m_notifier->positionChanged(this);
00336     if (m_watcher)
00337       m_watcher->positionChanged(this);
00338   }
00339 
00340   if (!edit.oldRange().isEmpty() && edit.start() <= m_lastPosition && edit.oldRange().end() >= m_lastPosition) {
00341     if (edit.start() == m_lastPosition) {
00342       // character deleted after
00343       if (m_notifier)
00344         emit m_notifier->characterDeleted(this, false);
00345       if (m_watcher)
00346         m_watcher->characterDeleted(this, false);
00347 
00348     } else if (edit.oldRange().end() == m_lastPosition) {
00349       // character deleted before
00350       if (m_notifier)
00351         emit m_notifier->characterDeleted(this, true);
00352       if (m_watcher)
00353         m_watcher->characterDeleted(this, true);
00354 
00355     } else {
00356       // position deleted
00357       if (m_notifier)
00358         emit m_notifier->positionDeleted(this);
00359       if (m_watcher)
00360         m_watcher->positionDeleted(this);
00361     }
00362   }
00363 
00364   if (!edit.newRange().isEmpty()) {
00365     if (*this == edit.newRange().start()) {
00366       // character inserted after
00367       if (m_notifier)
00368         emit m_notifier->characterInserted(this, false);
00369       if (m_watcher)
00370         m_watcher->characterInserted(this, false);
00371 
00372     } else if (*this == edit.newRange().end()) {
00373       // character inserted before
00374       if (m_notifier)
00375         emit m_notifier->characterInserted(this, true);
00376       if (m_watcher)
00377         m_watcher->characterInserted(this, true);
00378     }
00379   }
00380 
00381   if (!range() || !static_cast<KateSmartRange*>(range())->feedbackEnabled())
00382     m_lastPosition = *this;
00383 }
00384 
00385 void KateSmartCursor::shifted( )
00386 {
00387   Q_ASSERT(m_lastPosition != *this);
00388 
00389   // position changed
00390   if (m_notifier)
00391     emit m_notifier->positionChanged(this);
00392   if (m_watcher)
00393     m_watcher->positionChanged(this);
00394 
00395   if (!range() || !static_cast<KateSmartRange*>(range())->feedbackEnabled())
00396     m_lastPosition = *this;
00397 }
00398 
00399 void KateSmartCursor::migrate( KateSmartGroup * newGroup )
00400 {
00401   int lineNum = line();
00402   m_smartGroup = newGroup;
00403   m_line = lineNum - m_smartGroup->startLine();
00404 }
00405 
00406 void KateSmartCursor::setPosition( const KTextEditor::Cursor & pos )
00407 {
00408   if (pos.line() > kateDocument()->lastLine()) {
00409     kWarning() << "Attempted to set cursor position " << pos << " past end of document " << document()->documentRange();
00410     setPositionInternal(invalid(), false);
00411     return;
00412   }
00413 
00414   setPositionInternal(pos, false);
00415 }
00416 
00417 void KateSmartCursor::resetLastPosition( )
00418 {
00419   m_lastPosition = *this;
00420 }
00421 
00422 bool KateSmartCursor::hasNotifier( ) const
00423 {
00424   return m_notifier;
00425 }
00426 
00427 KTextEditor::SmartCursorWatcher * KateSmartCursor::watcher( ) const
00428 {
00429   return m_watcher;
00430 }
00431 
00432 void KateSmartCursor::unbindFromRange( )
00433 {
00434   setRange(0L);
00435 }
00436 
00437 void KateSmartCursor::setInternal( )
00438 {
00439   m_isInternal = true;
00440 }
00441 
00442 // kate: space-indent on; indent-width 2; replace-tabs on;

Kate

Skip menu "Kate"
  • Main Page
  • 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
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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