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