Writer documents act as a text-tables supplier, so you can directly retrieve text tables from Writer documents. Although text tables are enumerated as text content along with paragraphs (see Listing 6), tables are more typically obtained by name or by index (see Listing 37 and Figure 1 ).
Sub LookAtAIITables Dim oTables 'All of the text tables Dim s$ 'Work string Dim i% 'Index variable oTables = ThisComponent.TextTables REM First, access the tables based on index. s = "Tables By Index" & CHR$(10) For i = 0 To oTables.getCount() - 1 s = s & "Table " & (i+1) & " = " & oTables(i).Name & CHR$(10) Next s = s & CHR$(10) & CHR$(10) & "Text Tables By Name" & CHR$(10) s = s & Join(oTables.getElementNames(), CHR$(10)) MsgBox s, 0, "Tables" End Sub
Tip | Most named text content is retrieved, created, inserted, and disposed of in the same way. Learn to do this using tables and you'll be ready to use other named text content-bookmarks, for example. |
As with most text content, tables must be created by the document before they are inserted into the document. The macro in Listing 38 inserts a table named "SampleTable" if it does not exist, and removes it if it does.
Sub InsertDeleteTable Dim oTable 'Newly created table to insert Dim oTables 'All of the text tables Dim oInsertPoint 'Where the table will be inserted Dim sTableName as String sTableName = "SampleTable" oTables = ThisComponent.TextTables If oTables.hasByName(sTableName) Then oTable = oTables.getByName(sTableName) REM Although this seems like the correct way to remove text content, REM what if the table is not inserted into the primary document's REM text object? oTable.dispose() may be safer to use. ThisComponent.Text.removeTextContent(oTable) Else REM Let the document create the text table. oTable = ThisComponent.createInstance( "com.sun.star.text.TextTable" ) oTable.initialize(2, 3) 'Two rows, three columns REM If there is a bookmark named "InsertTableHere", then insert REM the table at that point. If this bookmark does not exist, REM then simply choose the very end of the document. If ThisComponent.getBookmarks().hasByName("InsertTableHere") Then oInsertPoint =_ ThisComponent.getBookmarks().getByName("InsertTableHere").getAnchor() Else oInsertPoint = ThisComponent.Text.getEnd() End If REM Now insert the text table at the end of the document. REM Note that the text object from the oInsertPoint text REM range is used rather than the document text object. oInsertPoint.getText().insertTextContent(oInsertPoint , oTable, False) REM The setData() object method works ONLY with numerical data. REM The setDataArray() object method, however, also allows strings. oTable.setDataArray(Array(Array(0, "One", 2), Array(3, "Four", 5))) oTable.setName(sTableName) End If End Sub
Tip | In general, it is better to set table properties before inserting the table into the document. This prevents screen flicker as the object is modified and then redrawn on the screen. Due to a bug in OOo 1.1.0, if Listing 38 is modified to set the data before inserting the table, the table name is modified and it contains an extra garbage character at the end. Be warned , however, that some text content, such as text field masters, cannot change their names after they have been inserted into a document. |
The macro in Listing 38 demonstrates many useful techniques:
A named text content is found and obtained. Notice that finding the table and the bookmark are very similar processes.
A bookmark is used.
A text table is created, initialized , and inserted at a location marked by a bookmark.
Text content is deleted, but perhaps dispose() should have been used.
A table is initialized with data.
The table name is set.
It is very important that you use the correct text object. It is possible that the text object in a text section or table cell is not the same as the text object returned by the document. Each text range is associated with a specific text object. Attempting to use object methods from one text object to operate on a text range associated with a different text object causes an error. In Listing 38, a table is removed, with the single line shown in Listing 39 .
ThisComponent.Text.removeTextContent(oTable)
The code in Listing 39 assumes that the table is contained in the document's text object. If the table is not contained in the primary document's text object, the code in Listing 39 will fail. Although Listing 39 will rarely fail, it will undoubtedly fail at the worst possible time. The macro works because the sample document was designed so that the table is inserted into the primary document's text object. Either solution shown in Listing 40 may be a better solution for deleting the table.
oTable.getAnchor().getText().removeTextContent(oTable) oTable.dispose()
The second time that a text object is used in Listing 38, it is obtained from the anchor returned from a bookmark-this is safe. See Listing 41 .
oInsertPoint.getText().insertTextContent(oInsertPoint , oTable, False)
If the text range oInsertPoint is not contained in the document's text object, attempting to insert the table using the document's text object will fail. Only you can decide how careful you need to be when accessing text objects. Consider the selected text framework. What text object is used to create text cursors and to compare text cursors ? Can you make the code more robust?
The methods supported by text tables are very similar to the methods supported by spreadsheets contained in Calc documents (see Chapter 14, "Calc Documents"). Table 18 summarizes the object methods supported by text tables.
Method | Description |
---|---|
autoFormat(name) | Apply the specified auto-format name to the table. |
createCursorByCellName(name) | XTextTableCursor positioned at the specified cell. |
createSortDescriptor() | Array of PropertyValues that specify the sort criteria. |
dispose() | Destroy a text object, which also removes it from the document. |
getAnchor() | Return a text range identifying where the table is anchored. This method allows text content to be added easily before or after a text table. |
getCellByName(name) | Return an XCell based on the cell name, such as "B3". |
getCellByPosition(col, row) | Numbering starts at zero. This has difficulties with complex tables. |
getCellNames() | String array of cell names contained in the table. |
getCellRangeByName(name) | XCellRange based on cell name, such as A1:B4. Fails if the name identifies a cell that has been split. |
getCellRangeByPosition(left, top, right, bottom) | XCellRange based on numeric range. |
getColumnDescriptions() | Array of strings describing the columns. Fails for complex tables. |
getColumns() | XTableColumns object enumerates columns by index. Also supports insertByIndex(idx, count) and remove ByIndex(idx, count). |
getData() | Get numerical data as a nested sequence of values (arrays in an array). Fails for complex tables. |
getDataArray() | Same as getData() but may contain String or Double. |
getName() | Get the table name as a string. |
getRowDescriptions() | Array of strings describing the rows. Fails for complex tables. |
getRows() | XTableRows object enumerates rows by index. Also supports insertByIndex(idx, count) and remove ByIndex(idx, count). |
initialize(rows, cols) | Set the numbers of rows and columns. Must be done before the table is inserted (see Listing 4). |
setColumnDescriptions(string()) | Set the column descriptions from an array of strings. |
setData(Double()) | Set numerical data as a nested sequence of values. Fails for complex tables. |
setDataArray(array()) | Same as setData() but may contain String or Double. |
setName(name) | Set the table name. |
setRowDescriptions(string()) | Set the row descriptions from an array of strings. |
sort(array()) | Sort the table based on a sort descriptor. |
Text table objects also support a variety of properties (see Table 19 ). Text tables support many of the same properties that are supported by paragraphs (see Table 5).
Property | Description |
---|---|
BreakType | Specify the type of break that is applied at the start of the table (see the BreakType attribute in Table 5). |
LeftMargin | Specify the left table margin in 0.01 mm as a Long Integer. Set the HoriOrient property to something other than FULL. |
RightMargin | Specify the right table margin in 0.01 mm as a Long Integer. Set the HoriOrient property to something other than FULL. |
HoriOrient | Specify the horizontal orientation using the com.sun.star.text.HoriOrientation constants. The default value is com.sun.star.text.HoriOrientation.FULL.
|
KeepTogether | If True, prevents page or column breaks between this table and the following paragraph or text table. |
Split | If False, the table will not split across two pages. |
PageDescName | If this string is set, a page break occurs before the paragraph, and the new page uses the given page style name (see PageDescName in Table 5). |
PageNumberOffset | If a page break occurs, specify a new page number (see Page NumberOffset in Table 5). |
RelativeWidth | Specify the width of the table relative to its environment as a Short Integer. |
IsWidthRelative | If True, the relative width is valid. |
RepeatHeadline | If True, the first row of the table is repeated on every new page. |
ShadowFormat | Specify the type, color , and size of the shadow (see ParaShadowFormat in Table 5). |
TopMargin | Specify the top table margin in 0.01 mm as a Long Integer. |
BottomMargin | Specify the bottom table margin in 0.01 mm as a Long Integer. |
BackTransparent | If True, the background color is transparent. |
Width | Specify the absolute table width as a Long Integer -this is a read-only property. |
ChartRowAsLabel | If True, the first row is treated as axis labels if a chart is created. |
ChartColumnAsLabel | If True, the first column is treated as axis labels if a chart is created. |
TableBorder | Specify the table borders in a com.sun.star.table.TableBorder structure. The structure contains numerous complicated properties:
|
TabIeColumnSeparators | Specify the width of each column with an array of table column separators. Each separator is a com.sun.star.text.TableColumnSeparator structure.
The width of a cell is defined by the position of the separator between adjacent cells. When two cells are merged, the separator is hidden, not removed. The Position values are relative to the text table TabIeColumnRelativeSum property. This property is valid for a table only if every row has the same structure. If they do not, obtain the separators from the individual row objects. |
TabIeColumnRelativeSum | Specify the sum of the column-width values used in TabIeColumnSeparators as a Short Integer. |
BackColor | Specify the paragraph background color as a Long Integer. |
BackGraphicURL | Specify the URL of the paragraph background graphic. |
BackGraphicFilter | Specify the name of the graphic filter for the paragraph background graphic. |
BackGraphicLocation | Specify the position of a background graphic (see ParaBackGraphicLocation in Table 5). |
Simply speaking, a text table is a set of rows and columns of text. All of the tables in this book are represented using simple text tables. OOo supports both simple and complex tables. As their name implies, in simple tables the table cells are laid out in a simple grid (see Table 20 ). Each column is labeled alphabetically starting with the letter A, and each row is labeled numerically starting with the number 1. The object method getCellByName() uses this name to return the specified cell. A similar object method, getCellByPosition(), returns the cell based on the column and row number. The column and row number are zero-based numbers, so requesting (1, 2) returns the cell named "B3".
A1 | B1 | C1 |
A2 | B2 | C2 |
A3 | B3 | C3 |
OOo supports more than just simple text tables. A text table contains rows, rows contain one or more cells (columns), and cells contain either text content or rows. In other words, adjacent cells may be merged, and individual cells may be merged either horizontally or vertically. The naming convention for complex tables is more complicated than for simple tables (see Table 21 ). In Table 21, the cell named B2 was split horizontally. The new name is a concatenation of the former cell name (B2) and the number of the new column and row index inside the original table cell, separated by dots.
Al | B1 | C1 | D1 | |
A2 | B2.1.1 | C2 | D2 | |
B2.1.2 | ||||
A3 | B3 | C3 | D3 | E3 |
A4 | B4 | C4 | ||
Tip | Not all object methods work with complex tables. For example, the object methods getData() and setData() cause an exception for complex tables. |
Although the object method getCellByName() works as expected for complex tables, getCellByPosition() is not able to return all of the cells because it allows only a column and a row number. Use the getCellNames() object method to return the names of the cells in a table (see Listing 42 ); you can then use the cell names to individually obtain each cell in the table.
MsgBox Join(oTable.getCellNames(), "")
The cells in a text table are very versatile objects capable of holding all types of data. The cell objects implement both the XText interface (see Table 2 near the beginning of this chapter) as well as the XCell interface (see Table 22 ).
Method | Description |
---|---|
getFormula() | The original string typed into the cell, even if it is not a formula. |
setFormula(String) | Set the cell's formula. Use setString() from the XText interface to set text. |
getValue() | Floating-point (Double) value of the cell. |
setValue(Double) | Set the floating-point value of the cell. |
getType() | Return a com.sun.star.table.CellContentType enumeration with valid values of EMPTY, VALUE, TEXT, and FORMULA. |
getError() | Long Integer error value. If the cell is not a formula, the error value is zero. |
Note | Tables are able to create a text table cursor with methods and properties specifically designed to traverse and select cells, and each individual cell is able to produce a text cursor that is local to the cell text object. |
Each cell object posses numerous properties. These properties are generally familiar because they are used in other objects. For example, the BackColor, BackGraphicFilter, BackGraphicLocation, BackGraphicURL, BackTransparent, BorderDistance, BottomBorder, BottomBorderDistance, LeftBorder, LeftBorderDistance, RightBorder, RightBorderDistance, TopBorder, and TopBorderDistance properties are defined for text tables in Table 19 and/or paragraph properties in Table 5. One of the more useful properties that is available only in the cell object, however, is CellName. This is useful to determine the location of the current cursor. The macro in Listing 43 demonstrates a few new manipulations for tables.
The text tables, rows, columns, and cells all support the BackColor property. Listing 43 sets the background color of the first row to a light gray, which is commonly used to mark headings.
Listing 43: SimpleTableManipulations is found in the Writer module in this chapter's source code files as SC13.sxw.
Sub SimpleTableManipulations Dim oTable 'Newly created table to insert Dim oTables 'All of the text tables Dim oInsertPoint 'Where the table will be inserted Dim sTableName as String sTableName = "SampleTable" oTables = ThisComponent.TextTables REM Remove the table if it exists! If oTables.hasByName(sTableName) Then ThisComponent.Text.removeTextContent(oTables.getByName(sTableName)) End If REM Let the document create the text table. oTable = ThisComponent.createInstance( "com.sun.star.text.TextTable" ) oTable.initialize(4, 3) 'Two rows, three columns REM If there is a bookmark named "InsertTableHere", then insert REM the table at that point. If this bookmark does not exist, REM then simply choose the very end of the document. If ThisComponent.getBookmarks().hasByName("InsertTableHere") Then oInsertPoint =_ ThisComponent.getBookmarks().getByName("InsertTableHere").getAnchor() Else oInsertPoint = ThisComponent.Text.getEnd() End If oInsertPoint.getText().insertTextContent(oInsertPoint , oTable, False) oTable.setDataArray(Array(Array("Name", "Score", "Test"),_ Array("Bob", 80, "CCW"), Array("Andy" , 80, "CCW"),_ Array("Jean" , 100, "CCI"))) oTable.setName(sTableName) REM Set the first row to have a gray background. oTable.getRows().getByIndex(0).BackColor = RGB(235, 235, 235) REM removeByIndex uses the same arguments as insertByIndex, namely REM the index at which to insert or remove followed by the number REM of rows to insert or remove. The following line inserts REM one row at index 4. oTable.getRows().insertByIndex(4, 1) REM Obtain the individual cells and set the values. oTable.getCellByName("A5").setString("Whil") oTable.getCellByName("B5").setValue(100) oTable.getCellByName("C5").setString("Advanced") End Sub
The insertByIndex(index, num) object method is used to insert new rows at the end of a table. Rows can also be inserted into the middle or at the start of a table.
Individual cells are retrieved by name; both numeric values and strings are set. Notice that strings are set using setString() rather than setFormula().
Tip | Although the Web-based OOo documentation makes no distinction between cells contained in text tables and cells contained in spreadsheets, the two cell types do not support the same property set. For example, the CellStyle, CellBackColor, and RotateAngle properties are not supported. |
Although text table cursors implement methods specific to traversing text tables, they are not significantly different from their text cursor counterparts in general functionality. You can select and manipulate ranges of cells, and set cell properties.
Tip | You cannot obtain a text table from the document and then simply insert it again at another location. |
Like text cursors, text table cursor movement methods accept a Boolean argument that indicates if the current selection should be expanded (True) or if the cursor should simply be moved (False). The movement methods also return a Boolean value indicating if the movement was successful. Table 23 contains the methods defined by the XTextTableCursor interface.
Method | Description |
---|---|
getRangeName() | Return the cell range selected by this cursor as a string. For example, "B3:D5". |
gotoCellByName(String, boolean) | Move the cursor to the cell with the specified name; return Boolean. |
goLeft(n, boolean) | Move the cursor left n cells; return Boolean. |
goRight(n, boolean) | Move the cursor right n cells; return Boolean. |
goUp(n, boolean) | Move the cursor up n cells; return Boolean. |
goDown(n, boolean) | Move the cursor down n cells; return Boolean. |
gotoStart(boolean) | Move the cursor to the top left cell. |
gotoEnd(boolean) | Move the cursor to the bottom right cell. |
mergeRange() | Merge the selected range of cells; return True for success. |
splitRange(n, boolean) | Create n (an integer) new cells in each cell selected by the cursor. For the Boolean, specify True to split horizontally, False for vertically. Returns True on success. |
Text table cursors are used to split and merge table cells. In general, I consider this to be the primary use of a text table cursor. You can use text table cursors to move around the table by using the methods in Table 23. The macro in Listing 44 obtains the table cell names, creates a cell cursor that contains the first table cell, and then moves the cursor to the last cell in the table. A cell range is created based on the range name, and then the entire table is selected by the current controller.
oCellNames = oTable.getCellNames() oCursor = oTable.createCursorByCellName(oCellNames(0)) oCursor.gotoCellByName(oCellNames(UBound(oCellNames())), True) oRange = oTable.getCellRangeByName(oCursor.getRangeName()) 'This may fail! Thiscomponent.getCurrentController.select(oRange)
Listing 44 demonstrates how to select all cells in a table by using a table cell cursor. You can then manipulate the entire table using the cursor. It may fail, however, in selecting the entire table in the current view. Table cell cursors have no problems with complex tables. The object methods supported by tables, however, do not all support complex tables. A notable example is the object method getCellRangeByName(), as used in Listing 44. This is very unfortunate because the view cursor is able to select text based on a cell range, but the table cannot return a cell range that has a split cell as one of the endpoints. For example, the cell range A1.2.1:C4 fails.
There is no easy method to duplicate an entire text table, either within a document or between documents (at least not as of OOo version 1.1.0). The general solution for such problems is to use the clipboard. First, use the view cursor or current controller to select the object that you want to copy. Then use a dispatcher to copy the object to the clipboard, move the view cursor where the object should be placed, and then use a dispatcher to paste the object from the clipboard.
As you may have guessed, the difficult part in this process is selecting the table with the view cursor. Although numerous people have tried and failed to solve this problem, a brilliant solution was provided by Paolo Mantovani, a contributor on the OOo mailing lists. Paolo starts by noting that selecting an entire table with the current controller places the view cursor at the start of the first cell (see Listing 45 ).
ThisComponent.CurrentController.select(oTable)
Although Listing 45 does not entirely solve the problem, it does provide a good start, because the view cursor is in the table at a known position. Paolo then provides a very succinct method to select the entire table (see Listing 46 ).
ThisComponent.CurrentController.select(oTable) oVCursor.gotoEnd(True) 'Move to the end of the current cell oVCursor.gotoEnd(True) 'Move to the end of the table
Tip | Remember to carefully test all code dealing with tables. A different solution proposed by Paolo-which failed-was to use goRight() and then goDown() based on the number of rows and columns. |
The macro in Listing 47 selects a table by name, copies the table to the clipboard, and then pastes it at the end of the document.
Sub CopyNamedTableToEnd(sName As String) Dim oTable 'Table to copy Dim oText 'Document's text object Dim oFrame 'Current frame to use with the dispatcher Dim oVCursor 'Current view cursor Dim oDispatcher 'Dispatcher for clipboard commands oVCursor = ThisComponent.CurrentController.getViewCursor() oText = ThisComponent.getText() oFrame = ThisComponent.CurrentController.Frame oDispatcher = createUnoService("com.sun.star.frame.DispatchHelper") If NOT ThisComponent.getTextTables().hasByName(sName) Then MsgBox "Sorry, the document does not contain table " & sName Exit Sub End If oTable = ThisComponent.getTextTables().getByName(sName) REM Place the cursor in the start of the first cell. REM This is very easy! ThisComponent.CurrentController.select(oTable) oVCursor.gotoEnd(True) 'Move to the end of the current cell. oVCursor.gotoEnd(True) 'Move to the end of the table. REM Copy the table to the clipboard. oDispatcher.executeDispatch(oFrame, ".uno:Copy", " , 0, Array()) REM Move the cursor to the end of the document and then paste the table. oVCursor.gotoRange(oText.getEnd(), False) oDispatcher.executeDispatch(oFrame, ".uno:Paste", "", 0, Array()) End Sub