From kde-commits Mon Jul 02 13:33:31 2012 From: Dominik Haumann Date: Mon, 02 Jul 2012 13:33:31 +0000 To: kde-commits Subject: [kate] part: add DocumentCursor class Message-Id: <20120702133331.11687A60D1 () git ! kde ! org> X-MARC-Message: https://marc.info/?l=kde-commits&m=134123625817087 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 automatical= ly. 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/documentcurso= r.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 + * Copyright (C) 2012 Dominik Haumann + * + * 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 Lice= nse + * 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 =3D position; + } else { + m_cursor =3D 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() =3D=3D 0; +} + +bool DocumentCursor::atEndOfLine() const +{ + return isValidTextPosition() && column() =3D=3D document()->lineLength(l= ine()); +} + +bool DocumentCursor::atStartOfDocument() const +{ + return line() =3D=3D 0 && column() =3D=3D 0; +} + +bool DocumentCursor::atEndOfDocument() const +{ + return m_cursor =3D=3D document()->documentEnd(); +} + +bool DocumentCursor::gotoNextLine() +{ + // only allow valid cursors + const bool ok =3D isValid() && (line() + 1 < document()->lines()); + = + if (ok) { + setPosition(Cursor(line() + 1, 0)); + } + + return ok; +} + +bool DocumentCursor::gotoPreviousLine() +{ + // only allow valid cursors + bool ok =3D (line() > 0) && (column() >=3D 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 =3D 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 =3D=3D Wrap && c.column() > lineLength) { + c.setColumn(lineLength); + } + + while (chars !=3D 0) { + if (chars > 0) { + if (wrapBehavior =3D=3D Wrap) { + int advance =3D qMin(lineLength - c.column(), chars); + = + if (chars > advance) { + if (c.line() + 1 >=3D document()->lines()) { + return false; + } + = + c.setPosition(c.line() + 1, 0); + chars -=3D advance + 1; // +1 because of end-of-line wrap + + // advanced one line, so cache correct line length again + lineLength =3D document()->lineLength(c.line()); + } else { + c.setColumn(c.column() + chars); + chars =3D 0; + } + } else { // NoWrap + c.setColumn(c.column() + chars); + chars =3D 0; + } + } else { + int back =3D qMin(c.column(), -chars); + if (-chars > back) { + if (c.line() =3D=3D 0) + return false; + + c.setPosition(c.line() - 1, document()->lineLength(c.line() - 1)); + chars +=3D back + 1; // +1 because of wrap-around at start-of-line + + // advanced one line, so cache correct line length again + lineLength =3D document()->lineLength(c.line()); + } else { + c.setColumn(c.column() + chars); + chars =3D 0; + } + } + } + + if (c !=3D 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 + * Copyright (C) 2012 Dominik Haumann + * + * 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 Lice= nse + * 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 +#include + +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 \ + * + * \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 =3D 0x0, ///< wrap at end of line + NoWrap =3D 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=3D (const DocumentCursor &); + + // + // convenience API + // + public: + + /** + * Returns whether the current position of this cursor is a valid posi= tion, + * i.e. whether line() >=3D 0 and column() >=3D 0. + * \return \e true , if the cursor position is valid, otherwise \e fal= se + */ + inline bool isValid() const { + return line() >=3D 0 && column() >=3D 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() <=3D do= cument()->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 lin= e. + * + * \return \e true if cursor is a valid text position and column()=3D0= , 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, o= therwise \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, otherwis= e \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, otherw= ise \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 po= sition + * 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 wra= pBehavior + * equals WrapBehavior::Wrap, the cursor is automatically wrapped to t= he + * 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 requeste= d, + * the cursor is not moved at all! + * + * \return \e true on success, otherwise \e false + */ + bool move(int chars, WrapBehavior wrapBehavior =3D 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, allo= wing 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 colum= n are \e equal. + */ + inline friend bool operator=3D=3D(const DocumentCursor& c1, const Docu= mentCursor& c2) + { return c1.document() =3D=3D c2.document() && c1.line() =3D=3D c2.l= ine() && c1.column() =3D=3D 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 colum= n are \e not equal. + */ + inline friend bool operator!=3D(const DocumentCursor& c1, const Docume= ntCursor& c2) + { return !(c1 =3D=3D 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 DocumentC= ursor& c2) + { return c1.line() > c2.line() || (c1.line() =3D=3D 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>=3D(const DocumentCursor& c1, const Docume= ntCursor& c2) + { return c1.line() > c2.line() || (c1.line() =3D=3D c2.line() && c1.= column() >=3D 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 DocumentC= ursor& c2) + { return !(c1 >=3D 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<=3D(const DocumentCursor& c1, const Docume= ntCursor& 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 *curso= r) { + 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 &curso= r) { + 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 #include #include @@ -3719,25 +3721,26 @@ bool KateDocument::findMatchingBracket( KTextEditor= ::Range& range, int maxLines int maxLine =3D qMin( range.start().line() + maxLines, documentEnd().lin= e() ); = range.end() =3D range.start(); - QScopedPointer cursor(newMovingCursor(range.s= tart())); - int validAttr =3D kateTextLine(cursor->line())->attribute(cursor->column= ()); + KTextEditor::DocumentCursor cursor(this); + cursor.setPosition(range.start()); + int validAttr =3D kateTextLine(cursor.line())->attribute(cursor.column()= ); = - while( cursor->line() >=3D minLine && cursor->line() <=3D maxLine ) { + while( cursor.line() >=3D minLine && cursor.line() <=3D maxLine ) { = - if (!cursor->move(searchDir)) + if (!cursor.move(searchDir)) return false; = - Kate::TextLine textLine =3D kateTextLine(cursor->line()); - if (textLine->attribute(cursor->column()) =3D=3D validAttr ) + Kate::TextLine textLine =3D kateTextLine(cursor.line()); + if (textLine->attribute(cursor.column()) =3D=3D validAttr ) { - /* Check for match */ - QChar c =3D textLine->at(cursor->column()); + // Check for match + QChar c =3D textLine->at(cursor.column()); if( c =3D=3D opposite ) { if( nesting =3D=3D 0 ) { if (searchDir > 0) // forward - range.end() =3D cursor->toCursor(); + range.end() =3D cursor.toCursor(); else - range.start() =3D cursor->toCursor(); + range.start() =3D cursor.toCursor(); return true; } nesting--;