[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kate] part: add DocumentCursor class
From:       Dominik Haumann <dhdev () gmx ! de>
Date:       2012-07-02 13:33:31
Message-ID: 20120702133331.11687A60D1 () git ! kde ! org
[Download RAW message or body]

Git commit bc03d699e6948771449f93012ccd2532eb9e0a60 by Dominik Haumann.
Committed on 02/07/2012 at 15:26.
Pushed by dhaumann into branch 'master'.

add DocumentCursor class

DocumentCursor extends Cursor by several functions to work with cursors
on a specific Document (e.g. wrapping cursor). However, it is not \
registered like the MovingCursor, meaning that its position is not updated \
automatically. DocumentCursor is much more efficient that MovingCursor, if \
you do not need the automatic maintaining of the correct position in the \
document.

For KDE5, KTextEditor::DocumentCursor will be moved to KTextEditor :)

CCMAIL: kwrite-devel@kde.org

M  +1    -0    part/CMakeLists.txt
A  +200  -0    part/document/documentcursor.cpp     [License: LGPL (v2+)]
A  +358  -0    part/document/documentcursor.h     [License: LGPL (v2+)]
M  +13   -10   part/document/katedocument.cpp

http://commits.kde.org/kate/bc03d699e6948771449f93012ccd2532eb9e0a60

diff --git a/part/CMakeLists.txt b/part/CMakeLists.txt
index 9b04d36..92c3fdd 100644
--- a/part/CMakeLists.txt
+++ b/part/CMakeLists.txt
@@ -71,6 +71,7 @@ dialogs/katedialogs.cpp
 document/katedocument.cpp
 document/katedocumenthelpers.cpp
 document/katebuffer.cpp
+document/documentcursor.cpp
 
 # undo
 undo/kateundo.cpp
diff --git a/part/document/documentcursor.cpp \
b/part/document/documentcursor.cpp new file mode 100644
index 0000000..c97ea3c
--- /dev/null
+++ b/part/document/documentcursor.cpp
@@ -0,0 +1,200 @@
+/* This file is part of the KDE project
+ * 
+ *  Copyright (C) 2010 Christoph Cullmann <cullmann@kde.org>
+ *  Copyright (C) 2012 Dominik Haumann <dhaumann@kde.org>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public \
License + *  along with this library; see the file COPYING.LIB.  If not, \
write to + *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth \
Floor, + *  Boston, MA 02110-1301, USA.
+ */
+
+#include "documentcursor.h"
+
+namespace KTextEditor {
+  
+DocumentCursor::DocumentCursor(KTextEditor::Document* document)
+  : m_document(document)
+  , m_cursor(KTextEditor::Cursor::invalid())
+{
+  // we require a valid document
+  Q_ASSERT(m_document);
+}
+
+KTextEditor::Document *DocumentCursor::document () const
+{
+  return m_document;
+}
+
+void DocumentCursor::setPosition (const KTextEditor::Cursor& position)
+{
+  if (position.isValid()) {
+    m_cursor = position;
+  } else {
+    m_cursor = KTextEditor::Cursor::invalid();
+  }
+}
+
+int DocumentCursor::line() const
+{
+  return m_cursor.line();
+}
+
+int DocumentCursor::column() const
+{
+  return m_cursor.column();
+}
+
+DocumentCursor::~DocumentCursor ()
+{
+}
+
+void DocumentCursor::setPosition (int line, int column)
+{
+  m_cursor.setPosition(line, column);
+}
+
+void DocumentCursor::setLine(int line)
+{
+  setPosition(line, column());
+}
+
+void DocumentCursor::setColumn(int column)
+{
+  setPosition(line(), column);
+}
+
+bool DocumentCursor::atStartOfLine() const
+{
+  return isValidTextPosition() && column() == 0;
+}
+
+bool DocumentCursor::atEndOfLine() const
+{
+  return isValidTextPosition() && column() == \
document()->lineLength(line()); +}
+
+bool DocumentCursor::atStartOfDocument() const
+{
+  return line() == 0 && column() == 0;
+}
+
+bool DocumentCursor::atEndOfDocument() const
+{
+  return m_cursor == document()->documentEnd();
+}
+
+bool DocumentCursor::gotoNextLine()
+{
+  // only allow valid cursors
+  const bool ok = isValid() && (line() + 1 < document()->lines());
+  
+  if (ok) {
+    setPosition(Cursor(line() + 1, 0));
+  }
+
+  return ok;
+}
+
+bool DocumentCursor::gotoPreviousLine()
+{
+  // only allow valid cursors
+  bool ok = (line() > 0) && (column() >= 0);
+  
+  if (ok) {
+    setPosition(Cursor(line() - 1, 0));
+  }
+
+  return ok;
+}
+
+bool DocumentCursor::move(int chars, WrapBehavior wrapBehavior)
+{
+  if (!isValid()) {
+    return false;
+  }
+
+  Cursor c(m_cursor);
+
+  // cache lineLength to minimize calls of KateDocument::lineLength(), as
+  // results in locating the correct block in the text buffer every time,
+  // which is relatively slow
+  int lineLength = document()->lineLength(c.line());
+
+  // special case: cursor position is not in valid text, then the algo \
does +  // not work for Wrap mode. Hence, catch this special case by \
setting +  // c.column() to the lineLength()
+  if (chars > 0 && wrapBehavior == Wrap && c.column() > lineLength) {
+    c.setColumn(lineLength);
+  }
+
+  while (chars != 0) {
+    if (chars > 0) {
+      if (wrapBehavior == Wrap) {
+        int advance = qMin(lineLength - c.column(), chars);
+        
+        if (chars > advance) {
+          if (c.line() + 1 >= document()->lines()) {
+            return false;
+          }
+          
+          c.setPosition(c.line() + 1, 0);
+          chars -= advance + 1; // +1 because of end-of-line wrap
+
+          // advanced one line, so cache correct line length again
+          lineLength = document()->lineLength(c.line());
+        } else {
+          c.setColumn(c.column() + chars);
+          chars = 0;
+        }
+      } else { // NoWrap
+        c.setColumn(c.column() + chars);
+        chars = 0;
+      }
+    } else {
+      int back = qMin(c.column(), -chars);
+      if (-chars > back) {
+        if (c.line() == 0)
+          return false;
+
+        c.setPosition(c.line() - 1, document()->lineLength(c.line() - 1));
+        chars += back + 1; // +1 because of wrap-around at start-of-line
+
+        // advanced one line, so cache correct line length again
+        lineLength = document()->lineLength(c.line());
+      } else {
+        c.setColumn(c.column() + chars);
+        chars = 0;
+      }
+    }
+  }
+
+  if (c != m_cursor) {
+    setPosition(c);
+  }
+  return true;
+}
+
+const Cursor& DocumentCursor::toCursor () const
+{
+  return m_cursor;
+}
+
+DocumentCursor::operator const Cursor& () const
+{
+  return m_cursor;
+}
+
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/part/document/documentcursor.h \
b/part/document/documentcursor.h new file mode 100644
index 0000000..56634af
--- /dev/null
+++ b/part/document/documentcursor.h
@@ -0,0 +1,358 @@
+/* This file is part of the KDE project
+ * 
+ *  Copyright (C) 2010 Christoph Cullmann <cullmann@kde.org>
+ *  Copyright (C) 2012 Dominik Haumann <dhaumann@kde.org>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public \
License + *  along with this library; see the file COPYING.LIB.  If not, \
write to + *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth \
Floor, + *  Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KTEXTEDITOR_DOCUMENT_CURSOR_H
+#define KTEXTEDITOR_DOCUMENT_CURSOR_H
+
+#include <ktexteditor/cursor.h>
+#include <ktexteditor/document.h>
+
+namespace KTextEditor {
+
+/**
+ * \short A Cursor which is bound to a specific Document.
+ *
+ * \section documentcursor_intro Introduction
+ * A DocumentCursor is an extension of the basic Cursor class.
+ * The DocumentCursor is bound to a specific Document instance.
+ * This way, the cursor povides additional functions like gotoNextLine(),
+ * gotoPreviousLine() and move() according to the WrapBehavior.
+ *
+ * The only difference to a MovingCursor is that the DocumentCursor's
+ * position does not automatically move on text manipulation.
+ *
+ * \section documentcursor_position Validity
+ *
+ * When constructing a DocumentCursor, a valid document pointer is \
required + * in the constructor. A null pointer will assert in debug \
builds. + * Further, a DocumentCursor should only be used as long as the \
Document + * exists, otherwise the DocumentCursor contains a dangling \
pointer to the + * previously assigned Document.
+ *
+ * \section documentcursor_example Example
+ *
+ * A DocumentCursor is created and used like this:
+ * \code
+ * KTextEditor::DocumentCursor docCursor(document);
+ * docCursor.setPosition(0, 0);
+ * docCursor.gotoNextLine();
+ * docCursor.move(5); // move 5 characters to the right
+ * \endcode
+ *
+ * \see KTextEditor::Cursor, KTextEditor::MovingCursor
+ *
+ * \author Dominik Haumann \<dhaumann@kde.org\>
+ *
+ * \todo KDE5: move to ktexteditor interface, use it in
+ *       MovingCursor::move() to avoid code duplication
+ */
+class DocumentCursor
+{
+  //
+  // sub types
+  //
+  public:
+    /**
+     * Wrap behavior for end of line treatement used in move().
+     */
+    enum WrapBehavior {
+      Wrap = 0x0,  ///< wrap at end of line
+      NoWrap = 0x1 ///< do not wrap at end of line
+    };
+
+  //
+  // Constructor
+  //
+  public:
+    DocumentCursor(KTextEditor::Document* document);
+
+  //
+  // stuff that needs to be implemented by editor part cusors
+  //
+  public:
+
+    /**
+     * Gets the document to which this cursor is bound.
+     * \return a pointer to the document
+     */
+    Document *document () const;
+
+    /**
+     * Set the current cursor position to \e position.
+     * If \e position is not valid, meaning that either its line < 0 or \
its +     * column < 0, then the document cursor is set to invalid(-1, -1).
+     *
+     * \param position new cursor position
+     */
+    void setPosition (const KTextEditor::Cursor& position);
+
+    /**
+     * Retrieve the line on which this cursor is situated.
+     * \return line number, where 0 is the first line.
+     */
+    int line() const;
+
+    /**
+     * Retrieve the column on which this cursor is situated.
+     * \return column number, where 0 is the first column.
+     */
+    int column() const;
+
+    /**
+     * Destruct the moving cursor.
+     */
+    ~DocumentCursor ();
+
+  //
+  // forbidden stuff
+  //
+  private:
+    /**
+     * no default constructor, as we need a document.
+     */
+    DocumentCursor ();
+
+    /**
+     * no copy constructor, don't allow this to be copied.
+     */
+    DocumentCursor (const DocumentCursor &);
+
+    /**
+     * no assignment operator, no copying around clever cursors.
+     */
+    DocumentCursor &operator= (const DocumentCursor &);
+
+  //
+  // convenience API
+  //
+  public:
+
+    /**
+     * Returns whether the current position of this cursor is a valid \
position, +     * i.e. whether line() >= 0 and column() >= 0.
+     * \return \e true , if the cursor position is valid, otherwise \e \
false +     */
+    inline bool isValid() const {
+      return line() >= 0 && column() >= 0;
+    }
+
+    /**
+     * Check whether the current position of this cursor is a valid text
+     * position.
+     * \return \e true , if the cursor is a valid text position, otherwise \
\e false +     */
+    inline bool isValidTextPosition() const {
+      return isValid() && line() < document()->lines() && column() <= \
document()->lineLength(line()); +    }
+
+    /**
+     * \overload
+     *
+     * Set the cursor position to \e line and \e column.
+     *
+     * \param line new cursor line
+     * \param column new cursor column
+     */
+    void setPosition (int line, int column);
+
+    /**
+     * Set the cursor line to \e line.
+     * \param line new cursor line
+     */
+    void setLine(int line);
+
+    /**
+     * Set the cursor column to \e column.
+     * \param column new cursor column
+     */
+    void setColumn(int column);
+
+    /**
+     * Determine if this cursor is located at column 0 of a valid text \
line. +     *
+     * \return \e true if cursor is a valid text position and column()=0, \
otherwise \e false. +     */
+    bool atStartOfLine() const;
+
+    /**
+     * Determine if this cursor is located at the end of the current line.
+     *
+     * \return \e true if the cursor is situated at the end of the line, \
otherwise \e false. +     */
+    bool atEndOfLine() const;
+
+    /**
+     * Determine if this cursor is located at line 0 and column 0.
+     *
+     * \return \e true if the cursor is at start of the document, \
otherwise \e false. +     */
+    bool atStartOfDocument() const;
+
+    /**
+     * Determine if this cursor is located at the end of the last line in \
the +     * document.
+     *
+     * \return \e true if the cursor is at the end of the document, \
otherwise \e false. +     */
+    bool atEndOfDocument() const;
+
+    /**
+     * Moves the cursor to the next line and sets the column to 0. If the \
cursor +     * position is already in the last line of the document, the \
cursor position +     * remains unchanged and the return value is \e false.
+     *
+     * \return \e true on success, otherwise \e false
+     */
+    bool gotoNextLine();
+
+    /**
+     * Moves the cursor to the previous line and sets the column to 0. If \
the +     * cursor position is already in line 0, the cursor position \
remains +     * unchanged and the return value is \e false.
+     *
+     * \return \e true on success, otherwise \e false
+     */
+    bool gotoPreviousLine();
+
+    /**
+     * Moves the cursor \p chars character forward or backwards. If \e \
wrapBehavior +     * equals WrapBehavior::Wrap, the cursor is automatically \
wrapped to the +     * next line at the end of a line.
+     *
+     * When moving backwards, the WrapBehavior does not have any effect.
+     * \note If the cursor could not be moved the amount of chars \
requested, +     *       the cursor is not moved at all!
+     *
+     * \return \e true on success, otherwise \e false
+     */
+    bool move(int chars, WrapBehavior wrapBehavior = Wrap);
+
+    /**
+     * Convert this clever cursor into a dumb one.
+     * @return normal cursor
+     */
+    const Cursor& toCursor () const;
+
+    /**
+     * Convert this clever cursor into a dumb one. Equal to toCursor, \
allowing to use implicit conversion. +     * @return normal cursor
+     */
+    operator const Cursor& () const;
+
+  //
+  // operators for: DocumentCursor <-> DocumentCursor
+  //
+    /**
+     * Equality operator.
+     *
+     * \note comparison between two invalid cursors is undefined.
+     *       comparison between an invalid and a valid cursor will always \
be \e false. +     *
+     * \param c1 first cursor to compare
+     * \param c2 second cursor to compare
+     * \return \e true, if c1's and c2's assigned document, line and \
column are \e equal. +     */
+    inline friend bool operator==(const DocumentCursor& c1, const \
DocumentCursor& c2) +      { return c1.document() == c2.document() && \
c1.line() == c2.line() && c1.column() == c2.column(); } +
+    /**
+     * Inequality operator.
+     * \param c1 first cursor to compare
+     * \param c2 second cursor to compare
+     * \return \e true, if c1's and c2's assigned document, line and \
column are \e not equal. +     */
+    inline friend bool operator!=(const DocumentCursor& c1, const \
DocumentCursor& c2) +      { return !(c1 == c2); }
+
+    /**
+     * Greater than operator.
+     * \param c1 first cursor to compare
+     * \param c2 second cursor to compare
+     * \return \e true, if c1's position is greater than c2's position,
+     *         otherwise \e false.
+     */
+    inline friend bool operator>(const DocumentCursor& c1, const \
DocumentCursor& c2) +      { return c1.line() > c2.line() || (c1.line() == \
c2.line() && c1.column() > c2.column()); } +
+    /**
+     * Greater than or equal to operator.
+     * \param c1 first cursor to compare
+     * \param c2 second cursor to compare
+     * \return \e true, if c1's position is greater than or equal to c2's
+     *         position, otherwise \e false.
+     */
+    inline friend bool operator>=(const DocumentCursor& c1, const \
DocumentCursor& c2) +      { return c1.line() > c2.line() || (c1.line() == \
c2.line() && c1.column() >= c2.column()); } +
+    /**
+     * Less than operator.
+     * \param c1 first cursor to compare
+     * \param c2 second cursor to compare
+     * \return \e true, if c1's position is greater than or equal to c2's
+     *         position, otherwise \e false.
+     */
+    inline friend bool operator<(const DocumentCursor& c1, const \
DocumentCursor& c2) +      { return !(c1 >= c2); }
+
+    /**
+     * Less than or equal to operator.
+     * \param c1 first cursor to compare
+     * \param c2 second cursor to compare
+     * \return \e true, if c1's position is lesser than or equal to c2's
+     *         position, otherwise \e false.
+     */
+    inline friend bool operator<=(const DocumentCursor& c1, const \
DocumentCursor& c2) +      { return !(c1 > c2); }
+
+    /**
+     * kDebug() stream operator. Writes this cursor to the debug output in \
a nicely formatted way. +     * @param s debug stream
+     * @param cursor cursor to print
+     * @return debug stream
+     */
+    inline friend QDebug operator<< (QDebug s, const DocumentCursor \
*cursor) { +      if (cursor)
+        s.nospace() << "(" << cursor->document() << ": " << cursor->line() \
<< ", " << cursor->column() << ")"; +      else
+        s.nospace() << "(null document cursor)";
+      return s.space();
+    }
+
+    /**
+     * kDebug() stream operator. Writes this cursor to the debug output in \
a nicely formatted way. +     * @param s debug stream
+     * @param cursor cursor to print
+     * @return debug stream
+     */
+    inline friend QDebug operator<< (QDebug s, const DocumentCursor \
&cursor) { +      return s << &cursor;
+    }
+
+  private:
+    KTextEditor::Document* m_document;
+    KTextEditor::Cursor m_cursor;
+};
+
+}
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/part/document/katedocument.cpp \
b/part/document/katedocument.cpp index 564a0da..3f90748 100644
--- a/part/document/katedocument.cpp
+++ b/part/document/katedocument.cpp
@@ -50,6 +50,8 @@
 #include "katescriptmanager.h"
 #include "kateswapfile.h"
 
+#include "documentcursor.h"
+
 #include <ktexteditor/attribute.h>
 #include <ktexteditor/plugin.h>
 #include <ktexteditor/loadsavefiltercheckplugin.h>
@@ -3719,25 +3721,26 @@ bool KateDocument::findMatchingBracket( \
KTextEditor::Range& range, int maxLines  int maxLine = qMin( \
range.start().line() + maxLines, documentEnd().line() );  
   range.end() = range.start();
-  QScopedPointer<KTextEditor::MovingCursor> \
                cursor(newMovingCursor(range.start()));
-  int validAttr = kateTextLine(cursor->line())->attribute(cursor->column());
 +  KTextEditor::DocumentCursor cursor(this);
+  cursor.setPosition(range.start());
+  int validAttr = kateTextLine(cursor.line())->attribute(cursor.column());
 
-  while( cursor->line() >= minLine && cursor->line() <= maxLine ) {
+  while( cursor.line() >= minLine && cursor.line() <= maxLine ) {
 
-    if (!cursor->move(searchDir))
+    if (!cursor.move(searchDir))
       return false;
 
-    Kate::TextLine textLine = kateTextLine(cursor->line());
-    if (textLine->attribute(cursor->column()) == validAttr )
+    Kate::TextLine textLine = kateTextLine(cursor.line());
+    if (textLine->attribute(cursor.column()) == validAttr )
     {
-      /* Check for match */
-      QChar c = textLine->at(cursor->column());
+      // Check for match
+      QChar c = textLine->at(cursor.column());
       if( c == opposite ) {
         if( nesting == 0 ) {
           if (searchDir > 0) // forward
-            range.end() = cursor->toCursor();
+            range.end() = cursor.toCursor();
           else
-            range.start() = cursor->toCursor();
+            range.start() = cursor.toCursor();
           return true;
         }
         nesting--;


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic