#ifndef QTEXTDOCUMENTUTILS_H
#define QTEXTDOCUMENTUTILS_H
/** \file qtextdocumentutils.h
*
* \brief A collection of utilities for dealing with QTextDocument structures
*
* The data and routines in this file serve a few purposes.
*
* First, it imports qtextdocumentlobs.h as its foundation.
* (See documentation on that file for more information on what it defines.)
*
* Second, QTextDocument and related classes are an excellent set of document utilities, but do
* not provide every last feature I would like for the Lurch word processor. Thus this file
* provides a collection of tools works with QTextBlocks and other document elements in ways that
* fill in the gaps.
*
* Third, it creates the QTextNode class for interacting with QTextDocuments using a hierarchical
* API rather than a QTextCursor-based API.
*/
#include <QTextBlock>
#include "qtextdocumentlobs.h"
/** \brief Ordered list of text list styles
*
* Bulleted list styles are given first, followed by numbered list styles.
* They are roughly from "outer levels of an outline" to "inner levels of an outline"
* order within those two categories.
*
* This is a comprehensive list of all text list styles.
*/
const QList<QTextListFormat::Style> getTextListStyles ();
/** \brief Given a list style, return its name in a human-readable format
*
* E.g., results like "Bullet" or "A." are returned.
*/
QString listFormatToName ( QTextListFormat::Style s );
/** \brief Given a style name, return its style datum
*
* This function is the inverse of listFormatToName().
*/
QTextListFormat::Style nameToListFormat ( QString name );
/** \brief Given a list style, this returns the next list style
*
* "Next" in this case is determined by the order in which the styles appear in
* textListStyles. The order wraps around in a circle, so "next" after the last is the first.
*/
QTextListFormat::Style nextListStyle ( QTextListFormat::Style s );
/** \brief Given a list style, this returns the previous list style
*
* This function is the inverse of nextListFormat(), including the wrapping around the list.
*/
QTextListFormat::Style previousListStyle ( QTextListFormat::Style s );
/** \brief Returns whether the given text block is the first block in a QTextList
*
* If \a b is not in a list, the result is false.
* If \a b is in a list but there are earlier items in the same list, the result is false.
* Otherwise, the result is true.
*
* \see setFirstInList(), isLaterInList()
*/
bool isFirstInList ( QTextBlock b );
/** \brief Returns whether the given text block is in a QTextList, but not the first block in it
*
* If \a b is not in a list, the result is false.
* If \a b is in a list and is that list's first block, the result is false.
* Otherwise, the result is true.
*
* \see isFirstInList()
*/
bool isLaterInList ( QTextBlock b );
/** \brief Sets the text block containing \a c to be the first block in a QTextList, if possible
*
* If that block is not in a list, or the list is a bulleted list instead of a numbered list,
* this function does nothing.
* If \a on is true, this function proceeds as follows.
* <ol>
* <li>If the block is already the first block in its list (as per isFirstInList()),
* this function does nothing.</li>
* <li>Otherwise, create a new QTextList with the same format as the list that \a b is
* currently in.</li>
* <li>For each block in the QTextList that the cursor's block is currenly in,
* starting with the cursor's block and
* proceeding from there to the end, remove them from their current list and add them
* to the newly created QTextList.</li>
* </ol>
* If \a on is false, this function proceeds as follows.
* <ol>
* <li>Find an earlier list of the same style and indentation level. If there is not one,
* this function does nothing. In particular, this will be true if the cursor's block
* is <i>not</i> the first item in a list.</li>
* <li>Otherwise, for each block in the QTextList containing the cursor, starting at
* the first item and moving forward, remove them from their current list and add them
* to the earlier list found in the previous step.</li>
* </ol>
* This function uses \a c to do the editing. You may wish
* to call beginEditBlock() beforehand and endEditBlock() afterwards in \a c, because this
* function may perform seveal edits, and does not call those functions.
* If you do not care about having this function use your own cursor for the editing,
* see setFirstInList(QTextBlock,bool); it also calls the begin and end functions for you.
*
* \see isFirstInList(), canSetFirstInList()
*/
void setFirstInList ( QTextCursor& c, bool on = true );
/** \brief A convenience function that calls setFirstInList(QTextCursor,bool) with a new cursor
*
* Creates a cursor in \a b, calls beginEditBlock() in it, calls
* setFirstInList(QTextCursor,bool) using that cursor, and calls endEditBlock() in the cursor.
*/
void setFirstInList ( QTextBlock b, bool on = true );
/** \brief Returns whether the function setFirstInList(\a b,\a on) will take any action
*
* This function can be useful in detecting whether to enable tools for restarting/joining
* numbered lists. See the documentation for setFirstInList(), which begins with several
* situations in which that function would take no action. If any of those situations holds,
* this function returns false; it returns true otherwise.
*
* \see setFirstInList()
*/
bool canSetFirstInList ( QTextBlock b, bool on = true );
/** \brief Whether the text block is part of a numbered list (as opposed to a bulleted list)
*
* If \a b is part of a list and that list is of an enumerated style (using numbers, letters,
* roman numerals, etc., as opposed to bullets, discs, or squares) then this function returns
* true. Otherwise it returns false.
*
* See the Qt documentation for QTextListFormat::Style.
*/
bool isNumberedList ( QTextBlock b );
/** \brief Finds an earlier text block that is in a QTextList that \a b could be joined to, if any
*
* If \a b does not pass the check isFirstInList(), this function returns an invalid block.
*
* If it does pass, then this function iterates backwards through the blocks in the document,
* starting with the one immediately before \a b and proceeding towards the front of the document.
*
* The first time a block is encountered that is a list item with the same indentation level as
* \a b, then one of two actions is taken. If that block's QTextList has a style the same as
* that of \a b, that block is returned, because it is a sensible list to which to join the list
* starting with \a b, should the user choose to do so. But if its style is different, then an
* invalid block is returned, because we can't join our list to that one.
*
* If no such block is encountered, an invalid block is returned.
*
* \see isFirstInList()
*/
QTextBlock previousMatchingList ( QTextBlock b );
/** \brief Ensure a list has the same total indentation level throughout (or break it up)
*
* Several of the routines defined in this file depend upon the assumption that all the blocks
* that share the same QTextList are at the same indentation level (as blocks, and therefore
* the same block-plus-list indentation level).
*
* The user can, of course, mess this up by selecting a subset of such list items and indenting
* it (or unindenting it). Not only does this violate the assumptions of the routines in this
* file, but it is also counterintuitive for the user. Indented levels of an outline (in most
* word processors) use different numbering than the outer levels; they do not keep the same
* numbering flow.
*
* Thus this function can be used to ensure that all the blocks of a list are at the same
* block indentation level. When it encounters blocks that are not at the same indentation level
* as the previous block in the same list, it does not alter the indentation level, but rather
* splits the list, using setFirstInList() with \a b = the second block and \a on = true.
*
* When a user changes the indentation level of a selection, this function can be called on all
* QTextList object relevant to that selection, to preserve the assumption mentioned above.
* A function that does just that is splitListsAtIndentChanges().
*
* \see setFirstInList()
*/
void splitListAtIndentChanges ( QTextList* list );
/** \brief Ensure all lists in the given range have the same total indentation level throughout
*
* For why you may want to ensure such a thing for a QTextList, read the documentation for
* splitListAtIndentChanges(). This function simply finds the set of QTextList objects that
* modify blocks that intersect the range.selection(),
* and applies splitListAtIndentChanges() to each.
*
* \see splitListAtIndentChanges()
*/
void splitListsAtIndentChanges ( QTextCursor range );
/** \brief Return the index converted into text form in the given style
*
* For instance, in the QTextListFormat::ListLowerAlpha style, the first index (zero-based) is "a",
* the second is "b", and so on. If the style instead were QTextListFormat::ListUpperRoman, the
* texts for the indices would be "I", "II", and so on. For bulleted lists, this function just
* returns the string "." as an indicator that a bullet (circle, square, or disc) is shown instead
* of an ordinal indicator.
*/
QString textForIndex ( int index, QTextListFormat::Style style );
/** \brief A class that unites QTextBlock, QTextFrame, and QTextFragment
*
* It is convenient to be able to speak of a node in a QTextDocument tree hierarchy, and yet the
* QTextDocument API does not provide a class for doing so. Instead, it provides QTextFrames,
* which construct most of the hierarchy, QTextBlocks, which are the second-to-lowest level of
* the hierarchy, and QTextFragments, which are the leaves. This class is simply a union of
* those three, for convenience purposes.
*
* At any time, at most one of isFragment(), isBlock(), and isFrame() will be true.
* At construction time, none of the three are true, but isValid() is false.
*/
class QTextNode : public QObject
{
Q_OBJECT
public:
/** \brief Constructs an uninitialized QTextNode
*
* It can be initialized later with operator=().
*/
QTextNode ();
/** \brief Constructs a QTextNode that represents the given fragment
*
* After this, isFragment() will be true and asFragment() will return \a f.
* If \a doc is provided, then this node knows the document in which it sits.
* (QTextFragments do not ordinarily have that information.) Such information is needed
* for functions that deal with this fragment's context, such as nextSibling(), address(),
* parent(), etc.
*/
QTextNode ( QTextFragment f, QTextDocument* doc = NULL );
/** \brief Constructs a QTextNode that represents the given block
*
* After this, isBlock() will be true and asBlock() will return \a b.
*/
QTextNode ( QTextBlock b );
/** \brief Constructs a QTextNode that represents the given frame
*
* After this, isFrame() will be true and asFrame() will return \a f.
* This node will return true from isValid() iff \a f is not NULL.
*/
QTextNode ( QTextFrame* f );
/** \brief Standard copy constructor, which simply uses operator=(QTextNode)
*/
QTextNode ( const QTextNode& other );
/** \brief Test whether this node is a QTextFragment
*/
bool isFragment () const;
/** \brief Test whether this node is a QTextBlock
*/
bool isBlock () const;
/** \brief Test whether this node is a QTextFrame
*/
bool isFrame () const;
/** \brief Test whether this object has been initialized with data
*
* This should always be equivalent to
* <code>isFragment() || isBlock() || isFrame()</code>.
*/
bool isValid () const;
/** \brief Convert this node to a fragment
*
* Returns a valid fragment iff isFragment() returns true.
*/
QTextFragment asFragment () const;
/** \brief Convert this node to a block
*
* Returns a valid block iff isBlock() returns true.
*/
QTextBlock asBlock () const;
/** \brief Convert this node to a frame
*
* Returns a valid fragment iff isFrame() returns true.
*/
QTextFrame* asFrame () const;
/** \brief The QTextDocument containing the block, frame, or fragment this object represents
*
* If this node isBlock() or isFrame(), then the block or frame represented knows its
* document, and that value is returned. Otherwise, if this node isFragment(), then this
* function returns the document provided when this object was constructed, with the
* QTextNode(QTextFragment,QTextDocument*) constructor. If none was provided, NULL is
* returned.
*/
QTextDocument* document () const;
/** \brief The parent node for this node in its document
*
* If this node isFragment(), this function returns a node pointing to the block containing
* that fragment, unless document() returns NULL, in which case this function will return
* an invalid node.
* If this node isBlock() or isFrame(), this function returns a node pointing to the frame
* containing that block or frame. If there is none, for instance if the frame is a
* document's root frame, then this function returns a QTextNode that does not pass
* isValid().
*/
QTextNode parent () const;
/** \brief The number of nodes under this node
*
* If this node isBlock(), this returns the number of fragments it contains.
* If this node isFrame(), this returns the total number of blocks and frames it contains.
* Otherwise, this function returns zero.
*
* \see child()
*/
uint numChildren () const;
/** \brief The node that is the \a ith under this node
*
* If this node isBlock(), this returns its \a ith fragment.
* If this node isFrame(), this returns its \a ith child, where blocks and frames are both
* counted as children, interleaved among one another as in the document. If there is no
* \a ith child, this returns an invalid node.
* If this node isFragment() or !isValid(), this returns an invalid node.
*
* \see numChildren()
*/
QTextNode child ( uint i = 0 ) const;
/** \brief Previous sibling node to this one, if any (invalid node otherwise)
*
* The previous sibling of a fragment will be the preceding fragment in the same block, if
* there is one. The previous sibling of a block or frame will be the preceding block or
* frame in the same parent frame, if there is one. If there is no previous sibling, an
* invalid node is returned. Note that the root frame has no previous sibling.
*
* If the node is invalid or its parent() cannot be computed, this function returns an
* invalid node.
*
* \see nextSibling(), hasNodeBefore(), hasBlockBefore()
*/
QTextNode previousSibling () const;
/** \brief Next sibling node to this one, if any (invalid node otherwise)
*
* The next sibling of a fragment will be the following fragment in the same block, if
* there is one. The next sibling of a block or frame will be the following block or
* frame in the same parent frame, if there is one. If there is no next sibling, an
* invalid node is returned. Note that the root frame has no next sibling.
*
* If the node is invalid or its parent() cannot be computed, this function returns an
* invalid node.
*
* \see previousSibling(), hasNodeAfter(), hasBlockAfter()
*/
QTextNode nextSibling () const;
/** \brief If parent() returns a valid node, this is the index of this node in that parent
*
* Indices are zero-based. Indices in a frame count both blocks and frames in sequence.
* If parent() does not return a valid node, this function returns -1.
*/
int indexInParent () const;
/** \brief A list of unsigned integers is an address into a QTextNode hierarchy
*
* \see convertAddress()
*/
typedef QList<uint> Address;
/** \brief The address of this object in its document
*
* The result is a list of integer indices \f$i_1,\ldots,i_n\f$ so that this node is equal
* to <code>QTextNode( document()->rootFrame() ).child( i_1 ). ... .child( i_n )</code>.
* The list is empty if <code>!isValid() || !parent().isValid()</code>.
*/
Address address () const;
/** \brief Same as address(), but the result is first passed through convertAddress(Address)
*/
LobAddress convertedAddress () const;
/** \brief The node that is at the address \a address under this node
*
* This function iterates the child() function through the entries in \a address, as
* described in the documentation for address(), until a QTextNode is found and returned.
* Note that if \a address does not respect the structure of the hierarchy under this node
* (e.g., asking for child 17 at a time when there are only 8, or asking for a child of
* a fragment) then an invalid QTextNode is returned.
*/
QTextNode index ( const Address& address ) const;
/** \brief Same as index(Address) except the parameter is first passed through
* convertAddress(LobAddress)
*/
QTextNode index ( const LobAddress& address ) const;
/** \brief Changes this object to refer to the fragment \a f
*
* \see operator==(), operator=(QTextBlock), operator=(QTextFrame*), operator=(QTextNode)
*/
QTextNode& operator= ( const QTextFragment& f );
/** \brief Changes this object to refer to the block \a b
*
* \see operator==(), operator=(QTextFragment), operator=(QTextFrame*), operator=(QTextNode)
*/
QTextNode& operator= ( const QTextBlock& b );
/** \brief Changes this object to refer to the frame \a f
*
* \see operator==(), operator=(QTextFragment), operator=(QTextBlock), operator=(QTextNode)
*/
QTextNode& operator= ( QTextFrame* f );
/** \brief Changes this object to refer to the same object to which \a n refers
*
* \see operator==(), operator=(QTextFragment), operator=(QTextBlock), operator=(QTextFrame*)
*/
QTextNode& operator= ( const QTextNode& n );
/** \brief Returns true iff this node and \a other refer to the same block, frame, or fragment
*
* If the two nodes are different types (e.g., one is a block and the other a fragment) then
* this returns false. If either is invalid, this returns false. Otherwise, it returns the
* result of the operator==() function used on the wrapped QTextBlock, QTextFragment, or
* QTextFrame objects.
*
* \see operator=(QTextNode),
* operator=(QTextFragment), operator=(QTextBlock), operator=(QTextFrame*)
*/
bool operator== ( const QTextNode& other ) const;
/** \brief The negation of operator==()
*/
bool operator!= ( const QTextNode& other ) const;
/** \brief Cursor position immediately before the first charcter in this node
*
* If this node is a fragment or block, this is equivalent to its position().
* If this node is a frame, this is equivalent to the position of its first block.
* If this node is invalid, this returns -1.
*
* It is called positionBefore() because cursor positions are always in between characters,
* and this cursor position is immediately before the first character of this node.
*
* \see positionAfter(), positionAtEnd()
*/
int positionBefore () const;
/** \brief Cursor position immediately after the last character in this node
*
* If this node is a fragment or any block other than the last one in the document,
* this is equivalent to its starting position() plus its length().
* If this node is the last block in the document, then this is its starting position()
* plus one less than its length(), because the cursor cannot be placed after the final
* newline of the final block in the document.
* If this node is a frame, then this function returns the positionAfter() that frame's
* final block.
*
* It is called positionAfter() because cursor positions are always in between characters,
* and this cursor position is immediately after the last character of this node.
*
* \see positionBefore(), positionAtEnd()
*/
int positionAfter () const;
/** \brief Cursor position immediately after the last character in this node's contents
*
* If this node is a fragment, then this function behaves exactly like positionAfter().
* If this node is a block or frame, then this function returns the cursor position
* immediately before the final newline of the block (or the last block in the frame).
* In most cases, this is one less than positionAfter(), but if the block in question is
* the last in the document, then the return value of this function is the same as that of
* positionAfter().
*
* \see positionBefore(), positionAfter()
*/
int positionAtEnd () const;
/** \brief Uses the given cursor to select this node
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* The selection runs from positionBefore() to positionAfter().
* If either of those is -1, this function does nothing.
*
* \see select(), goToStart(), goToEnd()
*/
void select ( QTextCursor& cursor );
/** \brief Selects this node with a new cursor and returns it
*
* The selection runs from positionBefore() to positionAfter().
* If either of those is -1, this function returns a newly constructed cursor, unpositioned.
*
* \see select(QTextCursor), goToStart(), goToEnd()
*/
QTextCursor select ();
/** \brief Uses the given cursor to select the contents of this node
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* This is only different from select(QTextCursor) if the node is a block or frame, in which
* case the difference is that the final newline is not included in this selection. If this
* node is or contains the last block in the document, then this operates the same as
* select(QTextCursor).
*
* \see select(QTextCursor), selectContents()
*/
void selectContents ( QTextCursor& cursor );
/** \brief Selects the contents of this node with a new cursor and returns it
*
* This is only different from select() if the node is a block or frame, in which
* case the difference is that the final newline is not included in this selection. If this
* node is or contains the last block in the document, then this operates the same as
* select().
*
* \see select(), selectContents(QTextCursor)
*/
QTextCursor selectContents ();
/** \brief Place \a cursor immediately before the first character of this node
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Sets the cursor position to positionBefore().
*
* \see goToStart(), goToEnd(), select()
*/
void goToStart ( QTextCursor& cursor );
/** \brief Create a cursor immediately before the first character of this node and return it
*
* The new cursor's position is positionBefore().
*
* \see goToStart(QTextCursor), goToEnd(), select()
*/
QTextCursor goToStart ();
/** \brief Place \a cursor immediately after the last character of this node
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Sets the cursor position to positionAfter().
*
* \see goToStart(), goToEnd(), select()
*/
void goToEnd ( QTextCursor& cursor );
/** \brief Create a cursor immediately after the last character of this node and return it
*
* The new cursor's position is positionAfter().
*
* \see goToEnd(QTextCursor), goToStart(), select()
*/
QTextCursor goToEnd ();
/** \brief Sets the QTextCharFormat of this node to \a format, if possible
*
* If this node is a fragment, this sets its character format with
* QTextCursor::setCharFormat().
* If this node is a block, this sets its block character format with
* QTextCursor::setBlockCharFormat().
*
* In all other cases, this function does nothing.
*
* \see setFormat(QTextBlockFormat), setFormat(QTextCharFormat,QTextCursor)
*/
void setFormat ( QTextCharFormat format );
/** \brief Sets the QTextCharFormat of this node to \a format, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Acts exactly like setFormat(QTextCharFormat), except uses \a cursor to do the work.
*/
void setFormat ( QTextCharFormat format, QTextCursor& cursor );
/** \brief Sets the QTextBlockFormat of this node to \a format, if possible
*
* If this node is a fragment or block, this sets its block format with
* QTextCursor::setBlockFormat().
*
* In all other cases, this function does nothing.
*
* \see setFormat(QTextCharFormat), setFormat(QTextBlockFormat,QTextCursor)
*/
void setFormat ( QTextBlockFormat format );
/** \brief Sets the QTextBlockFormat of this node to \a format, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Acts exactly like setFormat(QTextBlockFormat), except uses \a cursor to do the work.
*/
void setFormat ( QTextBlockFormat format, QTextCursor& cursor );
/** \brief Remove character formatting from the node
*
* This is equivalent to setFormat( QTextCharFormat() ).
*
* \see setFormat(QTextCharFormat), clearCharFormat(QTextCursor)
*/
void clearCharFormat ();
/** \brief Remove character formatting from the node using \a cursor
*
* This is equivalent to setFormat( QTextCharFormat(), cursor ).
*
* \see setFormat(QTextCharFormat,QTextCursor), clearCharFormat()
*/
void clearCharFormat ( QTextCursor& cursor );
/** \brief Remove block formatting from the node
*
* This is equivalent to setFormat( QTextBlockFormat() ).
*
* \see setFormat(QTextBlockFormat), clearBlockFormat(QTextCursor)
*/
void clearBlockFormat ();
/** \brief Remove block formatting from the node using \a cursor
*
* This is equivalent to setFormat( QTextBlockFormat(), cursor ).
*
* \see setFormat(QTextBlockFormat,QTextCursor), clearBlockFormat()
*/
void clearBlockFormat ( QTextCursor& cursor );
/** \brief Remove character and block formatting from the node
*
* This is equivalent to calling both clearCharFormat() and clearBlockFormat().
*/
void clearFormats ();
/** \brief Remove character and block formatting from the node using \a cursor
*
* This is equivalent to calling both clearCharFormat() and clearBlockFormat() with \a cursor.
*/
void clearFormats ( QTextCursor& cursor );
/** \brief Replace the given node with a text fragment with the given \a text and \a format
*
* If this node is a text fragment, its character format is changed to \a format and its
* contents are changed to \a text. Otherwise, this routine does nothing.
*
* \see append(), insertBefore(), insertAfter(), replaceWithBlock(), replaceWithFrame()
*/
void replace ( QString text, QTextCharFormat format = QTextCharFormat() );
/** \brief Performs the same action as replace(QString,QTextCharFormat), but using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*/
void replace ( QString text, QTextCharFormat format, QTextCursor& cursor );
/** \brief Append a fragment with the given \a text and \a format after this node
*
* If this node is a fragment, the new fragment is added immediately after this one, in the
* same block. If this node is a block, the new fragment is appended to the end of this
* block. Otherwise, this function does nothing.
*
* \see append(QString,QTextCharFormat,QTextCursor), prepend()
*/
void append ( QString text, QTextCharFormat format = QTextCharFormat() );
/** \brief Performs the same action as append(QString,QTextCharFormat), but using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* \see prepend()
*/
void append ( QString text, QTextCharFormat format, QTextCursor& cursor );
/** \brief Insert a fragment with the given \a text and \a format before this node
*
* If this node is a fragment, the new fragment is added immediately before this one, in the
* same block. If this node is a block, the new fragment is inserted at the start of this
* block. Otherwise, this function does nothing.
*
* \see prepend(QString,QTextCharFormat,QTextCursor), append()
*/
void prepend ( QString text, QTextCharFormat format = QTextCharFormat() );
/** \brief Performs the same action as prepend(QString,QTextCharFormat), but using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* \see append()
*/
void prepend ( QString text, QTextCharFormat format, QTextCursor& cursor );
/** \brief Delete the contents of this node
*
* If this node is a block, all its fragments are deleted, leaving an empty block. It will
* have the same block and character formatting (as a block) that it did before; only its
* contents are erased.
*
* If this node is a frame, all its contents except one block are deleted, and that one block
* will have its contents deleted so that it becomes an empty block. That block will also
* have no block or character formatting, even if the first/last block in the frame formerly
* had such formatting; the formatting of the block inside the frame is, for the purposes of
* this function, considered "contents" of the frame.
*
* The notion of "contents" doesn't make as much sence for fragments; if this node is a
* fragment, this function behaves exactly like remove(), including invalidating this node.
*
* \see clearContents(QTextCursor), remove(), isNecessaryBlock()
*/
void clearContents ();
/** \brief Delete the contents of this node, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Behaves exactly like clearContents(), but uses \a cursor to do the work.
*
* \see clearContents()
*/
void clearContents ( QTextCursor& cursor );
/** \brief Whether this node is a block that is necessary for the overall document structure
*
* A QTextDocument requires that adjacent to any QTextFrame, on both sides, there must be at
* least one QTextBlock. Also, inside any QTextFrame, there must be at least one QTextBlock.
* Thus there are some blocks in a document that may not be deleted (using the QTextCursor
* API, the only API for altering a QTextDocument, on which the QTextNode class is built).
* Thus it is important to be able to detect when certain nodes in a document are undeletable.
* This function does so by returning true iff the node is a block that cannot be deleted
* for one of the reasons just mentioned.
*
* \see remove()
*/
bool isNecessaryBlock () const;
/** \brief Remove this node from the document, if possible
*
* If isNecessaryBlock() is true about this node, it will not be removed.
* Otherwise, if it is a fragment, it will be deleted from its paragraph. If it is a block,
* it will be deleted from its frame, and if it is a frame, it will be deleted from its parent
* frame. Some of these deletions require careful use of the underlying QTextCursor API, but
* they are all handled carefully by this routine.
*
* If this node's parent() cannot be computed, this function does nothing.
*
* If the function completes successfully, then this object will be invalid.
*
* \see isNecessaryBlock(), remove(QTextCursor), isValid()
*/
void remove ();
/** \brief Remove this node from the document, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Behaves just as remove(), but uses \a cursor to do the work.
* In all cases, \a cursor will then be at the position immediately following the now-absent
* frame (i.e., at the beginning of the block that was the frame's next sibling).
*
* \see remove()
*/
void remove ( QTextCursor& cursor );
/** \brief Insert a block before this node, if possible
*
* This function takes no action if the given node is invalid or a text fragment, or if its
* parent() cannot be computed.
*
* If it is a block or frame with a parent, then a new block is inserted before this block
* or frame. The new block has no character format or block format specified, and no text
* fragments in it.
*
* \see insertBlockBefore(QTextCursor), insertBlockAfter(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void insertBlockBefore ();
/** \brief Insert a block before this node, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Does the same thing as insertBlockBefore(), but uses \a cursor to do the work.
* After the insertion, if successful, \a cursor will be inside the new block.
*
* \see insertBlockBefore(), insertBlockAfter(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void insertBlockBefore ( QTextCursor& cursor );
/** \brief Insert a block after this node, if possible
*
* This function takes no action if the given node is invalid or a text fragment, or if its
* parent() cannot be computed.
*
* If it is a block or frame with a parent, then a new block is inserted after this block
* or frame. The new block has no character format or block format specified, and no text
* fragments in it.
*
* \see insertBlockAfter(QTextCursor), insertBlockBefore(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void insertBlockAfter ();
/** \brief Insert a block after this node, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Does the same thing as insertBlockAfter(), but uses \a cursor to do the work.
* After the insertion, if successful, \a cursor will be inside the new block.
*
* \see insertBlockBefore(), insertBlockAfter(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void insertBlockAfter ( QTextCursor& cursor );
/** \brief Replace this node with an empty block, if possible
*
* This function takes no action if the given node is invalid or a text fragment, or if its
* parent() cannot be computed.
*
* If it is a block or frame with a parent, then a new block is inserted to replace this
* block or frame. The new block has no character format or block format specified, and no
* text fragments in it. (For a block, this is equivalent to clearContents() followed by
* clearFormats().)
*
* \see replaceWithBlock(QTextCursor), insertBlockAfter(), insertBlockBefore(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void replaceWithBlock ();
/** \brief Replace this node with an empty block, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Does the same thing as replaceWithBlock(), but uses \a cursor to do the work.
* After the insertion, if successful, \a cursor will be inside the new block.
*
* \see insertBlockBefore(), insertBlockAfter(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void replaceWithBlock ( QTextCursor& cursor );
/** \brief Insert a frame before this node, if possible
*
* This function takes no action unless this node is a block whose previous sibling is also
* a block. To insert a frame before another frame, you must first insert a block before the
* existing frame, and then insert a frame before that.
*
* If the above checks pass, then a new frame is inserted before this block, after its
* previous sibling.
* The new frame has in it just one block, unformatted and empty.
*
* The new frame has format \a format.
*
* \see insertBlockBefore(), insertBlockAfter(), replaceWithBlock(),
* insertFrameBefore(QTextCursor,QTextFrameFormat), insertFrameAfter(), replaceWithFrame()
*/
void insertFrameBefore ( QTextFrameFormat format = QTextFrameFormat() );
/** \brief Insert a frame before this node, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Does the same thing as insertFrameBefore(), but uses \a cursor to do the work.
* After the insertion, if successful, \a cursor will be inside the one block in the new
* frame.
*
* \see insertBlockBefore(), insertBlockAfter(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void insertFrameBefore ( QTextCursor& cursor, QTextFrameFormat format = QTextFrameFormat() );
/** \brief Insert a frame after this node, if possible
*
* This function takes no action unless this node is a block whose next sibling is also
* a block. To insert a frame after another frame, you must first insert a block after the
* existing frame, and then insert a frame after that.
*
* If the above checks pass, then a new frame is inserted after this block, before its
* next sibling.
* The new frame has in it just one block, unformatted and empty.
*
* \see insertBlockAfter(), insertBlockBefore(), replaceWithBlock(),
* insertFrameAfter(QTextCursor,QTextFrameFormat), insertFrameBefore(), replaceWithFrame()
*/
void insertFrameAfter ( QTextFrameFormat format = QTextFrameFormat() );
/** \brief Insert a frame after this node, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Does the same thing as insertFrameAfter(), but uses \a cursor to do the work.
* After the insertion, if successful, \a cursor will be inside the one block in the new
* frame.
*
* \see insertBlockBefore(), insertBlockAfter(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void insertFrameAfter ( QTextCursor& cursor, QTextFrameFormat format = QTextFrameFormat() );
/** \brief Replace this node with a new frame, if possible
*
* This function takes no action if the given node is invalid or a text fragment, or if its
* parent() cannot be computed. It also takes no action if this node is a block that does
* not have blocks both before and after it. This can be tested (and optionally remedied)
* by the caller using code like the following.
* \code
* if ( !myNode.previousSibling().isBlock() )
* myNode.insertBlockBefore();
* if ( !myNode.nextSibling().isBlock() )
* myNode.insertBlockAfter();
* myNode.replaceWithFrame();
* \endcode
*
* If none of the above criteria prevent it, then a new frame is inserted to replace this
* block or frame. The new frame has in it just one block, unformatted and empty.
* (For a frame, this is equivalent to clearContents().)
*
* \see insertBlockAfter(), insertBlockBefore(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame(QTextCursor,QTextFrameFormat)
*/
void replaceWithFrame ( QTextFrameFormat format = QTextFrameFormat() );
/** \brief Replace this node with a new frame, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Does the same thing as replaceWithFrame(), but uses \a cursor to do the work.
* After the insertion, if successful, \a cursor will be inside the one block in the new
* frame.
*
* \see insertBlockBefore(), insertBlockAfter(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void replaceWithFrame ( QTextCursor& cursor, QTextFrameFormat format = QTextFrameFormat() );
/** \brief Converts a LobAddress into a QTextNode hierarchy address
*
* For a LobAddress whose steps are all OmNode::AsChild steps, this converts each step into
* an integer (using its stepIndex()) and subtracts one from it. This is because Lob
* hierarchies that imitate QTextNode hierarchies have OpenMath symbols in them as their
* 0th indices, which do not exist in the QTextNode hierarchy. The resulting Address
* is returned.
*
* An empty list is returned in one of three cases.
* <ul>
* <li>if the LobAddress had no steps</li>
* <li>if the LobAddress was not exclusively composed of child steps</li>
* <li>if the LobAddress had any c0 steps, so that the subtraction of 1 cannot be done</li>
* </ul>
*/
static Address convertAddress ( const LobAddress& address );
/** \brief Converts a QTextNode hierarchy address into a LobAddress
*
* For an Address whose entries are \f$i_1,\ldots,i_n\f$, this creates a LobAddress
* whose \a jth step is an OmNode::AsChild step with stepIndex() equal to \f$i_j+1\f$.
* This is therefore a kind of inverse to convertAddress(LobAddress).
*/
static LobAddress convertAddress ( const Address& address );
/** \brief A simple string representation, mostly useful for debugging
*/
QString toString () const;
private slots:
/** \brief Notices when a wrapped QTextFrame is destroyed, and invalidates this object
*
* If this node refers to a QTextFrame, and that frame is destroyed, it is essential for this
* object to notice that fact and invalidate its internal pointer to the frame (thus making
* this node invalid). Otherwise, this object may attempt to call functions in the
* no-longer-existing frame, and result in the application's crashing.
*
* Noticing the frame's destruction is accomplished by connecting the frame's destroyed()
* signal to this slot.
*
* \see setFrame(), docWasDestroyed()
*/
void frameWasDestroyed ();
/** \brief Notices when this node's document is destroyed, and invalidates this object
*
* If this node knows about its document, and that document is destroyed, it is essential for
* this object to notice that fact and invalidate itself. Otherwise, this object may attempt
* to look up data in the document (using asBlock().text() or asFragment().text(), for
* example) and this has been known to cause crashes/hangs.
*
* Noticing the document's destruction is accomplished by connecting the document's
* destroyed() signal to this slot.
*
* \see setDoc(), frameWasDestroyed()
*/
void docWasDestroyed ();
private:
/** \brief Auxiliary routine that does the work of index(Address).
*/
QTextNode index ( const Address& address, int step ) const;
/** \brief Create a new QTextCursor on the document in which this node sits
*
* If there is no known document, return a default-constructed cursor.
*/
QTextCursor newCursor ();
/** \brief Stores \a f in this object's \a frame field, and sets up a frameWasDestroyed()
* connection
*
* This function proceeds as follows.
* <ol>
* <li>Assign \a f to \a frame.</li>
* <li>Disconnect any former connection to the frameWasDestroyed() slot.</li>
* <li>If \a frame is non-null, set up the connection documented in frameWasDestroyed().</li>
* </ol>
*/
void setFrame ( QTextFrame* f );
/** \brief Stores \a d in this object's \a doc field, and sets up a docWasDestroyed()
* connection
*
* This function proceeds as follows.
* <ol>
* <li>Assign \a d to \a doc.</li>
* <li>Disconnect any former connection to the docWasDestroyed() slot.</li>
* <li>If \a doc is non-null, set up the connection documented in docWasDestroyed().</li>
* </ol>
*/
void setDoc ( QTextDocument* d );
/** \brief This node is a fragment iff this object is valid.
*/
QTextFragment fragment;
/** \brief This node is a block iff this object is valid.
*/
QTextBlock block;
/** \brief This node is a frame iff this pointer is non-null.
*/
QTextFrame* frame;
/** \brief If this node is a fragment, this pointer may refer to its document.
*
* Or it may be null, if that information is not available.
*/
QTextDocument* doc;
};
#endif // QTEXTDOCUMENTUTILS_H