[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