[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