Cursors


Cursors

The capability to enumerate the entire text content is primarily used for tasks such as exporting a document, because exporting requires all of the content to be accessed in sequential order. A more typical method for accessing and manipulating a document involves the use of cursors. A TextCursor is a TextRange, which can be moved within a Text object. In other words, a text cursor not only can specify a single point in the text, but it also can encompass a range of text. You can use the cursor movement methods to reposition the cursor and to expand the section of selected text. Two primary types of text cursors are available in OOo: cursors that are view cursors (see Table 9 ) and cursors that are not (see Table 11 ).

Table 9: In general, view cursors are not related to text ranges or XTextCursor.

Cursor

Description

com.sun.star.view.XViewCursor

Simple cursor with basic movement methods that work in both text and tables.

com.sun.star.text.XTextViewCursor

Derived from XTextCursor, this describes a cursor in a text document's view. It supports only very simple movements.

com.sun.star.view.XLineCursor

Defines line-related methods; this interface is not derived from a text range.

com.sun.star.text.XPageCursor

Defines page-related methods; this interface is not derived from a text range.

com.sun.star.view.XScreenCursor

Defines methods to move up and down one screen at a time.

View Cursors

As its name implies, the view cursor deals with the visible cursor. In a single document window, you see one view at a time. Analogously, you can have one view cursor at a time. View cursors support commands that are directly related to viewing. To move a cursor one line at a time, or one screen at a time, requires a view cursor. The view cursor knows how the text is displayed (see Table 10 ).

Table 10: Object methods related to view cursors.

Defined

Method

Description

XViewCursor

goDown(n, Boolean)

Move the cursor down by n lines.

XViewCursor

goUp(n, Boolean)

Move the cursor up by n lines.

XViewCursor

goLeft(n, Boolean)

Move the cursor left by n characters .

XViewCursor

goRight(n, Boolean)

Move the cursor right by n characters.

XTextViewCursor

isVisible()

Return True if the cursor is visible.

XTextViewCursor

setVisible(Boolean)

Show or hide the cursor.

XTextViewCursor

getPosition()

Return a com.sun.star.awt.Point structure specifying the cursor's coordinates relative to the top-left position of the first page of the document.

XLineCursor

isAtStartOfLine()

Return True if the cursor is at the start of the line.

XLineCursor

isAtEndOfLine()

Return True if the cursor is at the end of the line.

XLineCursor

gotoEndOfLine(Boolean)

Move the cursor to the end of the current line.

XLineCursor

gotoStartOfLine(Boolean)

Move the cursor to the start of the current line.

XPageCursor

jumpToFirstPage()

Move the cursor to the first page.

XPageCursor

jumpToLastPage()

Move the cursor to the last page.

XPageCursor

jumpToPage(n)

Move the cursor to the specified page.

XPageCursor

getPage()

Return the current page as a Short Integer.

XPageCursor

jumpToNextPage()

Move the cursor to the next page.

XPageCursor

jumpToPreviousPage()

Move the cursor to the previous page.

XPageCursor

jumpToEndOfPage()

Move the cursor to the end of the current page.

XPageCursor

jumpToStartOfPage()

Move the cursor to the start of the current page.

XScreenCursor

screenDown()

Scroll the view forward by one visible page.

XScreenCursor

screenUp()

Scroll the view backward by one visible page.

Most of the cursor movement methods accept a Boolean argument that determines whether the text range of the cursor is expanded (True) or whether the cursor is simply moved to the new position (False). In other words, if the Boolean expression is False, the cursor is moved to the new position and no text is selected by the cursor. The description of the Boolean variable is assumed and not explicitly stated for each of the cursor movement methods in Table 10. Another commonality with the movement methods is that they return a Boolean value. A True value means that the movement worked, and a False value means that the movement failed. A movement request fails if it cannot be completed successfully. It is not possible, for example, to move the cursor down if it is already at the end of the document. The screen cursor is obtained from the document's current controller (see Listing 13 ).

Listing 13: ScrollDown One Screen is found in the Writer module in this chapter's source code files as SC13.sxw.
start example
 Sub ScrollDownOneScreen   REM Get the view cursor from the current controller   ThisComponent.currentController.getViewCursor().screenDown() End Sub 
end example
 

A more typical use of the view cursor is to insert some special text content at the current cursor position. The macro in Listing 14 inserts the character with Unicode value 257 ("a" with a bar over it) at the current cursor position. This type of macro is typically associated with a keystroke to insert special characters that are not present on the keyboard. The macro in Listing 14 is short and simple, yet very useful.

Listing 14: Insert the character with Unicode value 257 at the current cursor.
start example
 Sub InsertControlCharacterAtCurrentCursor   Dim oViewCursor As Object   oViewCursor = ThisComponent.CurrentController.getViewCursor()   oViewCursor.getText.insertString(oViewCursor.getStart(), CHR$(257), False) End Sub 
end example
 

Text (Non-View) Cursors

The view cursor knows how the data is displayed, but doesn't know about the data itself. Text cursors (non-view cursors), however, know a lot about the data but very little about how it is displayed. For example, view cursors do not know about words or paragraphs, and text cursors do not know about lines, screens, or pages (see Table 11).

Table 11: Text cursor interfaces all implement the XTextCursor interface.

Cursor

Description

com.sun.star.text.XTextCursor

The primary text cursor that defines simple movement methods.

com.sun.star.text.XWordCursor

Provides word-related movement and testing methods.

com.sun.star.text.XSentenceCursor

Provides sentence -related movement and testing methods.

com.sun.star.text.XParagraphCursor

Provides paragraph-related movement and testing methods.

com.sun.star.text.XTextViewCursor

Derived from XTextCursor, this describes a cursor in a text document's view.

Note  

Text cursors and view cursors have some overlap. The XTextViewCursor is derived from the XTextCursor so it supports the XTextCursor methods. This does not provide functionality with respect to the underlying data such as word-related or paragraph-related methods (see Table 12).

Table 12: Object methods related to text cursors.

Defined

Method

Description

XTextCursor

collapseToStart()

Set the end position to the start position.

XTextCursor

collapseToEnd()

Set the start position to the end position.

XTextCursor

isCollapsed()

Return True if the start and end positions are the same.

XTextCursor

goLeft(n, Boolean)

Move the cursor left by n characters.

XTextCursor

goRight(n, Boolean)

Move the cursor right by n characters.

XTextCursor

gotoStart(Boolean)

Move the cursor to the start of the text.

XTextCursor

gotoEnd(Boolean)

Move the cursor to the end of the text.

XTextCursor

gotoRange(XTextRange, Boolean)

Move or expand the cursor to the TextRange.

XWordCursor

isStartOfWord()

Return True if at the start of a word.

XWordCursor

isEndOfWord()

Return True if at the end of a word.

XWordCursor

gotoNextWord(Boolean)

Move to the start of the next word.

XWordCursor

gotoPreviousWord(Boolean)

Move to the end of the previous word.

XWordCursor

gotoEndOfWord(Boolean)

Move to the end of the current word.

XWordCursor

gotoStartOfWord(Boolean)

Move to the start of the current word.

XSentenceCursor

isStartOfSentence()

Return True if at the start of a sentence.

XSentenceCursor

isEndOfSentence()

Return True if at the end of a sentence.

XSentenceCursor

gotoNextSentence(Boolean)

Move to the start of the next sentence.

XSentenceCursor

gotoPreviousSentence(Boolean)

Move to the end of the previous sentence.

XSentenceCursor

gotoEndOfSentence(Boolean)

Move to the end of the current sentence.

XSentenceCursor

gotoStartOfSentence(Boolean)

Move to the start of the current sentence.

XParagraphCursor

isStartOfParagraph()

True if at the start of a paragraph.

XParagraphCursor

isEndOfParagraph()

True if at the end of a paragraph.

XParagraphCursor

gotoNextParagraph(Boolean)

Move to the start of the next paragraph.

XParagraphCursor

gotoPreviousParagraph(Boolean)

Move to the end of the previous paragraph.

XParagraphCursor

gotoEndOfParagraph(Boolean)

Move to the end of the current paragraph.

XParagraphCursor

gotoStartOfParagraph(Boolean)

Move to the start of the current paragraph.

The word cursor, sentence cursor, and paragraph cursor all define essentially identical object methods (see Table 12 ). The XTextViewCursor interface is listed in Table 10 so it is omitted from Table 12.

Using Cursors to Traverse Text

Although traversing text using a cursor is not inherently difficult, I struggled with cursors for a long time before I finally realized that I had a very basic and yet simple misunderstanding. A very common, and yet subtly incorrect, method of traversing text content using cursors is shown in Listing 15 . This macro attempts to move the cursor from one paragraph to the next, selecting one paragraph at a time. Typically, something would be done to manipulate or modify the paragraph, such as setting the paragraph style.

Listing 15: Example of incorrect use of cursors: This code misses the last paragraph in the document.
start example
 Dim oCursor REM Create a text cursor oCursor = ThisComponent.Text.createTextCursor() REM Start at the beginning of the document. REM This is the same as the start of the first document. oCursor.gotoStart(False) REM And this is where things go wrong! REM The cursor now spans from the start of the REM first paragraph to the start of the second paragraph. Do While oCursor.gotoNextParagraph(True)   REM Process the paragraph here!   REM Now, deselect all of the text, leaving the cursor at the   REM start of the next paragraph.   oCursor.goRight(0, False) Loop 
end example
 
Note  

I produced incorrect code as shown in Listing 15 before I understood cursors.

The problem in Listing 15 is that the method gotoNextParagraph(True) causes the cursor to extend its selection from the start of one paragraph to the start of the next. The first problem with selecting from the start of one paragraph to the start of the next is that more than just one paragraph is selected. If the two different paragraphs do not have the same paragraph style, the ParaStyleName property will return an empty string rather than a value. The second problem is that when the cursor (as shown in Listing 15) is positioned at the start of the last paragraph, it is not possible to go to the next paragraph because the next paragraph does not exist. Therefore, the statement "gotoNextParagraph(True)" returns False and the last paragraph is never processed . The code in Listing 16 demonstrates one correct method of traversing all of the paragraphs using a cursor.

Listing 16: The correct way to use cursors.
start example
 Dim oCursor REM Create a text cursor. oCursor = ThisComponent.Text.createTextCursor() REM Start at the beginning of the document. REM This is the same as the start of the first document. oCursor.gotoStart(False) Do   REM The cursor is already positioned at the start   REM of the current paragraph, so select the entire paragraph.   oCursor.gotoEndOfParagraph(True)   REM Process the paragraph here!   REM The Loop statement moves to the next paragraph and   REM it also deselects the text in the cursor. Loop While oCursor.gotoNextParagraph(False) 
end example
 
Tip  

The last paragraph is very important. Do not position the cursor in front of a word or paragraph and then use "gotoNextWord(True)" or "gotoNextParagraph(True)" and expect that you selected only one word or paragraph. If more than one paragraph is selected, a paragraph-specific property is not available unless both paragraphs have the same value for the property.

The point is to align the cursor over the current paragraph, and then iterate aligned with the paragraphs, rather than ever looking ahead to the next paragraph while in the midst of dealing with the cursor.

Tip  

You can use cursors and enumeration to traverse an entire document and obtain all of the paragraphs. With OOo 1.1.0, using a cursor was five times faster than using an enumerator.

It is very simple to write an iterator that traverses the text while counting the words, sentences, and paragraphs (see Listing 17 ).

Listing 17: CollectSimpleStatistics is found in the Writer module in this chapter's source code files as SC13.sxw.
start example
 Sub CollectSimpleStatistics   Dim oCursor   Dim nPars As Long   Dim nSentences As Long   Dim nWords As Long   REM Create a text cursor.   oCursor = ThisComponent.Text.createTextCursor()   oCursor.gotoStart(False)   Do     nPars = nPars + 1   Loop While oCursor.gotoNextParagraph(False)   oCursor.gotoStart(False)   Do     nSentences = nSentences + 1   Loop While oCursor.gotoNextSentence(False)   oCursor.gotoStart(False)   Do     nWords = nWords + 1   Loop While oCursor.gotoNextWord(False)   MsgBox "Paragraphs: " & nPars & CHR$(10) &_      "Sentences: " & nSentences & CHR$(10) &_      "Words: " & nWords & CHR$(10), 0, "Doc Statistics" End Sub 
end example
 
Bug  

As of OOo 1.1.0, the methods gotoNextSentence() and gotoNextWord() are not reliable. My assessment is that the paragraph cursor works great, the word cursor frequently stumbles at the end of lines, and the sentence cursor is highly unstable. To count words correctly, visit Andrew Brown's useful macro Web site: http://www.darwinwars.com/lunatic/ bugs /oo_macros.html .

I saw a beautiful example using both view cursors and text cursors on the OpenOffice.org dev mailing list. A user wanted a macro that added line breaks to each line in the current paragraph. This problem includes the following interesting problems:

  • Find the paragraph that contains the view cursor. The user assumed that the view cursor was at the start of the paragraph already. Listing 18 does not make this assumption and instead specifically moves the cursor to the beginning of the current paragraph.

    Listing 18: LineBreaksInParagraph is found in the Writer module in this chapter's source code files as SC13.sxw.

    start example
     Sub LineBreaksInParagraph   Dim oText       'Save typing ThisComponent.Text   Dim oViewCursor 'Save typing ThisComponent.CurrentController.getViewCursor()   Dim oTextCursor 'Created text cursor   Dim oSaveCursor 'In case I want to restore the view cursor   oText = ThisComponent.Text   REM You require a view cursor because only the view knows where a line ends.   oViewCursor = ThisComponent.CurrentController.getViewCursor()   REM You require a text cursor so that you know where the paragraph ends.   REM Too bad the view cursor is not a paragraph cursor.   oTextCursor = oText.createTextCursorByRange(oViewCursor)   REM You only need this if you want to restore the view cursor   oSaveCursor = oText.createTextCursorByRange(oViewCursor)   REM Move the cursor to the start of the current paragraph   REM so that the entire paragraph can be processed.   If NOT oTextCursor.isStartOfParagraph() Then     oTextCursor.gotoStartOfParagraph(False)     oViewCursor.gotoRange(oTextCursor, False) End If   REM Now visit each line of the paragraph.   Do While True     REM Only the view cursor knows where the end of the line is     REM because this is a formatting issue and not determined by     REM punctuation.     oViewCursor.gotoEndOfLine(false)     REM Move with the view cursor to the end of the current line     REM and then see if you're at the end of the paragraph.     oTextCursor.gotoRange(oViewCursor, False)     REM Check for end of paragraph BEFORE inserting the line break.     If oTextCursor.isEndOfParagraph() Then Exit Do     REM Insert a line break at the current view cursor.     oText.insertControlCharacter(oViewCursor,_        com.sun.star.text.ControlCharacter.LINE_BREAK, false)   Loop   REM If you only want to move the cursor away from the end of the   REM current paragraph, then this will suffice. oViewCursor.goRight(1, False)   REM I want to restore the view cursor location, however.   REM oViewCursor.gotoRange(oSaveCursor, False) End Sub 
    end example
     
  • Both a view cursor and a non-view cursor are required to move through the text. The view cursor knows where a line ends, but it does not know where the paragraph starts or ends. A non-view cursor knows about paragraphs, but the view cursor does not.

Listing 18 moves the view cursor around on the screen, demonstrating how to change its location.

Accessing Content using Cursors

I have a habit of inspecting the objects returned to me by OOo. While inspecting a view cursor, I noticed that the view cursor contained useful undocumented properties. According to the developers at Sun, these will eventually be documented. Some of the properties that I noticed include: Cell, DocumentIndex, DocumentlndexMark, Endnote, Footnote, ReferenceMark, Text, TextField, TextFrame, TextSection, and TextTable. If a cursor is in a text table, the cursor's TextTable property is not empty. If a cursor is on a text field, the cursor's TextField property is not empty. These special properties are empty if they are not relevant. I first used these undocumented properties when I was asked how to determine if the view cursor was positioned in a text table, and if so, what cell contains the view cursor. See Listing 19 .

Listing 19: Testing view cursor properties.
start example
 If NOT IsEmpty(oViewCursor.TextTable) Then 
end example
 

Cursors provide the ability to quickly find objects in close proximity to something else. For example, I was recently asked how to find a text field contained in the current paragraph-the current paragraph is defined as the paragraph containing the view cursor. It's easy to obtain the view cursor, and you can use a paragraph cursor to move around the current paragraph.

My first attempt called the createContentEnumeration("com.sun.star.text.TextContent") object method on the cursor. This creates an enumeration of text content, enumerating objects such as inserted buttons . I had mistakenly assumed that this would include text fields in the enumeration. My second attempt to find a text field, which was successful, uses the createEnumeration() object method. The createEnumeration() method returns an enumeration of the paragraphs contained in the cursor. Enumerating the content contained in a paragraph provides access to the text field. My final attempt, which was also successful, moves the cursor to the start of the document and then moves through the paragraph one location at a time looking for a text field. The macro in Listing 20 demonstrates all of the methods that I used to try to find a text field in the current paragraph.

Listing 20: TextFieldInCurrentParagraph is found in the Writer module in this chapter's source code files as SC13.sxw.
start example
 Sub TextFieldInCurrentParagraph   Dim oEnum         'Cursor enumerator   Dim oSection      'Current section   Dim oViewCursor   'Current view cursor   Dim oTextCursor   'Created text cursor   Dim oText         'Text object in current document   Dim s$   oText = ThisComponent.Text   oViewCursor = ThisComponent.CurrentController.getViewCursor()   REM Obtain the view cursor, and then select the paragraph   REM containing the view cursor.   oTextCursor = oText.createTextCursorByRange(oViewCursor)   REM Move to the start of the paragraph as a single point.   oTextCursor.gotoStartOfParagraph(False)   REM Move to the end of the current paragraph and expand the   REM selection so that the entire paragraph is selected.   oTextCursor.gotoEndOfParagraph(True)   REM I want to enumerate the text content contained by this text cursor.   REM Although this will find inserted drawing objects, such as the shape   REM used by a button, it will not find a text field!   oEnum = oTextCursor.createContentEnumeration("com.sun.star.text.TextContent")   Do While oEnum.hasMoreElements()     oSection = oEnum.nextElement()     Print "Enumerating TextContent: " & oSection.ImplementationName   Loop   REM And this enumerates the paragraphs that are contained in   REM the text cursor.   oEnum = oTextCursor.createEnumeration()   Dim v   Do While oEnum.hasMoreElements()     v = oEnum.nextElement()     Dim oSubSection     Dim oSecEnum     REM Now create an enumeration of the paragraphs.     REM We can enumerate the sections of the paragraph to     REM obtain the text field and other paragraph content.     oSecEnum = v.createEnumeration()     s = "Enumerating section type: " & v.ImplementationName     Do While oSecEnum.hasMoreElements()         oSubSection = oSecEnum.nextElement()         s = s & CHR$(10) & oSubSection.TextPortionType         If oSubSection.TextPortionType = "TextField" Then           s = s & " <== Type " & oSubSection.TextField.ImplementationName         End If     Loop     MsgBox s, 0, "Enumerate Single Paragraph"   Loop   REM And this is yet another way to find the text field.   REM Start at the beginning of the paragraph and then move the cursor   REM through it, looking for text fields.   oTextCursor.gotoStartOfParagraph(False)   Do While oTextCursor.goRight(1, False) AND NOT oTextCursor.isEndOfParagraph()     If NOT IsEmpty(oTextCursor.TextField) Then       Print "It is NOT empty, you can use the text field"     End If   Loop End Sub 
end example
 
Warning  

As unintuitive as it sounds, it is not only possible-but common-for the end of a text range to come before the start of a text range. The order of the start and end is primarily an issue when dealing with text selected by a human user but may also occur due to the movement of a text cursor while expanding the text range.

As the object methods getStart() and getEnd() imply, it's possible for a text range to represent a single point. It's also possible that the start position comes after the end position. Unexpected start and end positions are typically a problem while dealing with selected text. When you select text using a mouse or your keyboard, the initial selection point is generally the start location. Moving the final selection point toward the start of the document causes the end position to occur before the start position in the text range. The same behavior may occur while manually moving and expanding a cursor. This behavior is not documented, but it has been observed in OOo 1.1.0, and like all undocumented behavior, it may change at any time. The text object can compare two ranges (see Table 13 ), but the text object must contain both text ranges-a text range has the getText() object method to return the text object that contains the text range.

Table 13: Methods defined by the com.sun.star.text.XTextRangeCompare interface.

Method

Description

compareRegionStarts(XTextRange, XTextRange)

  • Return 1 if the first range starts before the second.

  • Return 0 if the first range starts at the same position as the second.

  • Return -1 if the first range starts after the second.

compareRegionEnds(XTextRange, XTextRange)

  • Return 1 if the first range ends before the second.

  • Return 0 if the first range ends at the same position as the second.

  • Return -1 if the first range ends after the second.




OpenOffice.org Macros Explained
OpenOffice.org Macros Explained
ISBN: 1930919514
EAN: 2147483647
Year: 2004
Pages: 203

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net