Index: src/examples/src/org/apache/poi/xssf/usermodel/examples/Outlining.java =================================================================== --- src/examples/src/org/apache/poi/xssf/usermodel/examples/Outlining.java (revision 0) +++ src/examples/src/org/apache/poi/xssf/usermodel/examples/Outlining.java (revision 0) @@ -0,0 +1,75 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.xssf.usermodel.examples; + +import java.io.FileOutputStream; + +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +public class Outlining { + + public static void main(String[]args) throws Exception{ + Outlining o=new Outlining(); + o.groupRowColumn(); + o.collapseExpandRowColumn(); + } + + + private void groupRowColumn() throws Exception{ + Workbook wb = new XSSFWorkbook(); + Sheet sheet1 = wb.createSheet("new sheet"); + + sheet1.groupRow( 5, 14 ); + sheet1.groupRow( 7, 14 ); + sheet1.groupRow( 16, 19 ); + + sheet1.groupColumn( (short)4, (short)7 ); + sheet1.groupColumn( (short)9, (short)12 ); + sheet1.groupColumn( (short)10, (short)11 ); + + FileOutputStream fileOut = new FileOutputStream("outlining.xlsx"); + wb.write(fileOut); + fileOut.close(); + + } + + private void collapseExpandRowColumn()throws Exception{ + Workbook wb2 = new XSSFWorkbook(); + Sheet sheet2 = wb2.createSheet("new sheet"); + sheet2.groupRow( 5, 14 ); + sheet2.groupRow( 7, 14 ); + sheet2.groupRow( 16, 19 ); + + sheet2.groupColumn( (short)4, (short)7 ); + sheet2.groupColumn( (short)9, (short)12 ); + sheet2.groupColumn( (short)10, (short)11 ); + + + sheet2.setRowGroupCollapsed( 7, true ); + //sheet1.setRowGroupCollapsed(7,false); + + sheet2.setColumnGroupCollapsed( (short)4, true ); + sheet2.setColumnGroupCollapsed( (short)4, false ); + + FileOutputStream fileOut = new FileOutputStream("outlining_collapsed.xlsx"); + wb2.write(fileOut); + fileOut.close(); + } +} \ No newline at end of file Index: src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/ColumnHelper.java =================================================================== --- src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/ColumnHelper.java (revision 711264) +++ src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/ColumnHelper.java (working copy) @@ -223,9 +223,12 @@ toCol.setHidden(fromCol.getHidden()); toCol.setBestFit(fromCol.getBestFit()); toCol.setStyle(fromCol.getStyle()); - if(fromCol.getOutlineLevel()!=0){ + if(fromCol.isSetOutlineLevel()){ toCol.setOutlineLevel(fromCol.getOutlineLevel()); } + if(fromCol.isSetCollapsed()){ + toCol.setCollapsed(true); + } } public void setColBestFit(long index, boolean bestFit) { Index: src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java =================================================================== --- src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java (revision 711264) +++ src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java (working copy) @@ -18,11 +18,20 @@ package org.apache.poi.xssf.usermodel; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; -import java.io.InputStream; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + import javax.xml.namespace.QName; +import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.POIXMLException; import org.apache.poi.hssf.util.PaneInformation; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.Footer; @@ -31,20 +40,43 @@ import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellReference; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; import org.apache.poi.xssf.model.CommentsTable; import org.apache.poi.xssf.usermodel.helpers.ColumnHelper; -import org.apache.poi.POIXMLDocumentPart; -import org.apache.poi.POIXMLException; -import org.apache.poi.util.POILogger; -import org.apache.poi.util.POILogFactory; +import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlOptions; -import org.apache.xmlbeans.XmlException; +import org.openxml4j.exceptions.InvalidFormatException; import org.openxml4j.opc.PackagePart; import org.openxml4j.opc.PackageRelationship; import org.openxml4j.opc.PackageRelationshipCollection; -import org.openxml4j.exceptions.InvalidFormatException; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.*; import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBreak; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCols; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDrawing; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTHeaderFooter; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTHyperlink; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTMergeCell; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTMergeCells; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOutlinePr; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageBreak; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageMargins; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageSetUpPr; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPane; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPrintOptions; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRow; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSelection; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetData; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetFormatPr; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetPr; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetView; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetViews; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPane; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPaneState; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.WorksheetDocument; /** @@ -320,6 +352,16 @@ * Creates a split (freezepane). Any existing freezepane or split pane is overwritten. * @param colSplit Horizonatal position of split. * @param rowSplit Vertical position of split. + */ + public void createFreezePane(int colSplit, int rowSplit) { + createFreezePane( colSplit, rowSplit, colSplit, rowSplit ); + } + + + /** + * Creates a split (freezepane). Any existing freezepane or split pane is overwritten. + * @param colSplit Horizonatal position of split. + * @param rowSplit Vertical position of split. * @param topRow Top row visible in bottom pane * @param leftmostColumn Left column visible in right pane. */ @@ -344,15 +386,8 @@ CTSelection sel = ctView.addNewSelection(); sel.setPane(pane.getActivePane()); } + - /** - * Creates a split (freezepane). Any existing freezepane or split pane is overwritten. - * @param colSplit Horizonatal position of split. - * @param rowSplit Vertical position of split. - */ - public void createFreezePane(int colSplit, int rowSplit) { - createFreezePane( colSplit, rowSplit, colSplit, rowSplit ); - } /** * Creates a new comment for this sheet. You still @@ -388,20 +423,22 @@ * @param leftmostColumn Left column visible in right pane. * @param activePane Active pane. One of: PANE_LOWER_RIGHT, * PANE_UPPER_RIGHT, PANE_LOWER_LEFT, PANE_UPPER_LEFT - * @see #PANE_LOWER_LEFT - * @see #PANE_LOWER_RIGHT - * @see #PANE_UPPER_LEFT - * @see #PANE_UPPER_RIGHT + * @see org.apache.poi.ss.usermodel.Sheet#PANE_LOWER_LEFT + * @see org.apache.poi.ss.usermodel.Sheet#PANE_LOWER_RIGHT + * @see org.apache.poi.ss.usermodel.Sheet#PANE_UPPER_LEFT + * @see org.apache.poi.ss.usermodel.Sheet#PANE_UPPER_RIGHT */ public void createSplitPane(int xSplitPos, int ySplitPos, int leftmostColumn, int topRow, int activePane) { createFreezePane(xSplitPos, ySplitPos, leftmostColumn, topRow); - getPane().setActivePane(STPane.Enum.forInt(activePane)); - } - + getPane().setState(STPaneState.SPLIT); + getPane().setActivePane(STPane.Enum.forInt(activePane)); + } + public XSSFComment getCellComment(int row, int column) { if (sheetComments == null) return null; else return sheetComments.findCellComment(row, column); } + public XSSFHyperlink getHyperlink(int row, int column) { String ref = new CellReference(row, column).formatAsString(); @@ -1226,16 +1263,345 @@ } public void setColumnGroupCollapsed(short columnNumber, boolean collapsed) { - // TODO Auto-generated method stub + if (collapsed) { + collapseColumn(columnNumber); + } else { + expandColumn(columnNumber); + } + } + private void collapseColumn(short columnNumber) { + CTCols cols = worksheet.getColsArray(0); + CTCol col = columnHelper.getColumn(columnNumber, false); + int colInfoIx = columnHelper.getIndexOfColumn(cols, col); + if (colInfoIx == -1) { + return; + } + // Find the start of the group. + int groupStartColInfoIx = findStartOfColumnOutlineGroup(colInfoIx); + + CTCol columnInfo = cols.getColArray(groupStartColInfoIx); + + // Hide all the columns until the end of the group + int lastColMax = setGroupHidden(groupStartColInfoIx, columnInfo + .getOutlineLevel(), true); + + // write collapse field + setColumn((int) (lastColMax + 1), null, 0, null, null, Boolean.TRUE); + } - public void setRowGroupCollapsed(int row, boolean collapse) { - // TODO Auto-generated method stub + private void setColumn(int targetColumnIx, Short xfIndex, Integer style, + Integer level, Boolean hidden, Boolean collapsed) { + CTCols cols = worksheet.getColsArray(0); + CTCol ci = null; + int k = 0; + for (k = 0; k < cols.sizeOfColArray(); k++) { + CTCol tci = cols.getColArray(k); + if (tci.getMin() >= targetColumnIx + && tci.getMax() <= targetColumnIx) { + ci = tci; + break; + } + if (tci.getMin() > targetColumnIx) { + // call column infos after k are for later columns + break; // exit now so k will be the correct insert pos + } + } + if (ci == null) { + // okay so there ISN'T a column info record that covers this column + // so lets create one! + CTCol nci = CTCol.Factory.newInstance(); + nci.setMin(targetColumnIx); + nci.setMax(targetColumnIx); + unsetCollapsed(collapsed, nci); + this.columnHelper.addCleanColIntoCols(cols, nci); + return; + } + + boolean styleChanged = style != null + && ci.getStyle() != style.intValue(); + boolean levelChanged = level != null + && ci.getOutlineLevel() != level.intValue(); + boolean hiddenChanged = hidden != null + && ci.getHidden() != hidden.booleanValue(); + boolean collapsedChanged = collapsed != null + && ci.getCollapsed() != collapsed.booleanValue(); + boolean columnChanged = levelChanged || hiddenChanged + || collapsedChanged || styleChanged; + if (!columnChanged) { + // do nothing...nothing changed. + return; + } + + if (ci.getMin() == targetColumnIx && ci.getMax() == targetColumnIx) { + // ColumnInfo ci for a single column, the target column + unsetCollapsed(collapsed, ci); + return; + } + + if (ci.getMin() == targetColumnIx || ci.getMax() == targetColumnIx) { + // The target column is at either end of the multi-column ColumnInfo + // ci + // we'll just divide the info and create a new one + if (ci.getMin() == targetColumnIx) { + ci.setMin(targetColumnIx + 1); + } else { + ci.setMax(targetColumnIx - 1); + k++; // adjust insert pos to insert after + } + CTCol nci = columnHelper.cloneCol(cols, ci); + nci.setMin(targetColumnIx); + unsetCollapsed(collapsed, nci); + this.columnHelper.addCleanColIntoCols(cols, nci); + + } else { + // split to 3 records + CTCol ciStart = ci; + CTCol ciMid = columnHelper.cloneCol(cols, ci); + CTCol ciEnd = columnHelper.cloneCol(cols, ci); + int lastcolumn = (int) ci.getMax(); + + ciStart.setMax(targetColumnIx - 1); + + ciMid.setMin(targetColumnIx); + ciMid.setMax(targetColumnIx); + unsetCollapsed(collapsed, ciMid); + this.columnHelper.addCleanColIntoCols(cols, ciMid); + + ciEnd.setMin(targetColumnIx + 1); + ciEnd.setMax(lastcolumn); + this.columnHelper.addCleanColIntoCols(cols, ciEnd); + } } + private void unsetCollapsed(boolean collapsed, CTCol ci) { + if (collapsed) { + ci.setCollapsed(collapsed); + } else { + ci.unsetCollapsed(); + } + } + /** + * Sets all adjacent columns of the same outline level to the specified + * hidden status. + * + * @param pIdx + * the col info index of the start of the outline group + * @return the column index of the last column in the outline group + */ + private int setGroupHidden(int pIdx, int level, boolean hidden) { + CTCols cols = worksheet.getColsArray(0); + int idx = pIdx; + CTCol columnInfo = cols.getColArray(idx); + while (idx < cols.sizeOfColArray()) { + columnInfo.setHidden(hidden); + if (idx + 1 < cols.sizeOfColArray()) { + CTCol nextColumnInfo = cols.getColArray(idx + 1); + + if (!isAdjacentBefore(columnInfo, nextColumnInfo)) { + break; + } + + if (nextColumnInfo.getOutlineLevel() < level) { + break; + } + columnInfo = nextColumnInfo; + } + idx++; + } + return (int) columnInfo.getMax(); + } + + private boolean isAdjacentBefore(CTCol col, CTCol other_col) { + return (col.getMax() == (other_col.getMin() - 1)); + } + + private int findStartOfColumnOutlineGroup(int pIdx) { + // Find the start of the group. + CTCols cols = worksheet.getColsArray(0); + CTCol columnInfo = cols.getColArray(pIdx); + int level = columnInfo.getOutlineLevel(); + int idx = pIdx; + while (idx != 0) { + CTCol prevColumnInfo = cols.getColArray(idx - 1); + if (!isAdjacentBefore(prevColumnInfo, columnInfo)) { + break; + } + if (prevColumnInfo.getOutlineLevel() < level) { + break; + } + idx--; + columnInfo = prevColumnInfo; + } + return idx; + } + + private int findEndOfColumnOutlineGroup(int colInfoIndex) { + CTCols cols = worksheet.getColsArray(0); + // Find the end of the group. + CTCol columnInfo = cols.getColArray(colInfoIndex); + int level = columnInfo.getOutlineLevel(); + int idx = colInfoIndex; + while (idx < cols.sizeOfColArray() - 1) { + CTCol nextColumnInfo = cols.getColArray(idx + 1); + if (!isAdjacentBefore(columnInfo, nextColumnInfo)) { + break; + } + if (nextColumnInfo.getOutlineLevel() < level) { + break; + } + idx++; + columnInfo = nextColumnInfo; + } + return idx; + } + + private void expandColumn(int columnIndex) { + CTCols cols = worksheet.getColsArray(0); + CTCol col = columnHelper.getColumn(columnIndex, false); + int colInfoIx = columnHelper.getIndexOfColumn(cols, col); + + int idx = findColInfoIdx((int) col.getMax(), colInfoIx); + if (idx == -1) { + return; + } + + // If it is already expanded do nothing. + if (!isColumnGroupCollapsed(idx)) { + return; + } + + // Find the start/end of the group. + int startIdx = findStartOfColumnOutlineGroup(idx); + int endIdx = findEndOfColumnOutlineGroup(idx); + + // expand: + // colapsed bit must be unset + // hidden bit gets unset _if_ surrounding groups are expanded you can + // determine + // this by looking at the hidden bit of the enclosing group. You will + // have + // to look at the start and the end of the current group to determine + // which + // is the enclosing group + // hidden bit only is altered for this outline level. ie. don't + // uncollapse contained groups + CTCol columnInfo = cols.getColArray(endIdx); + if (!isColumnGroupHiddenByParent(idx)) { + int outlineLevel = columnInfo.getOutlineLevel(); + boolean nestedGroup = false; + for (int i = startIdx; i <= endIdx; i++) { + CTCol ci = cols.getColArray(i); + if (outlineLevel == ci.getOutlineLevel()) { + ci.unsetHidden(); + if (nestedGroup) { + nestedGroup = false; + ci.setCollapsed(true); + } + } else { + nestedGroup = true; + } + } + } + // Write collapse flag (stored in a single col info record after this + // outline group) + setColumn((int) columnInfo.getMax() + 1, null, null, null, + Boolean.FALSE, Boolean.FALSE); + } + + private boolean isColumnGroupHiddenByParent(int idx) { + CTCols cols = worksheet.getColsArray(0); + // Look out outline details of end + int endLevel = 0; + boolean endHidden = false; + int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup(idx); + if (endOfOutlineGroupIdx < cols.sizeOfColArray()) { + CTCol nextInfo = cols.getColArray(endOfOutlineGroupIdx + 1); + if (isAdjacentBefore(cols.getColArray(endOfOutlineGroupIdx), + nextInfo)) { + endLevel = nextInfo.getOutlineLevel(); + endHidden = nextInfo.getHidden(); + } + } + // Look out outline details of start + int startLevel = 0; + boolean startHidden = false; + int startOfOutlineGroupIdx = findStartOfColumnOutlineGroup(idx); + if (startOfOutlineGroupIdx > 0) { + CTCol prevInfo = cols.getColArray(startOfOutlineGroupIdx - 1); + + if (isAdjacentBefore(prevInfo, cols + .getColArray(startOfOutlineGroupIdx))) { + startLevel = prevInfo.getOutlineLevel(); + startHidden = prevInfo.getHidden(); + } + + } + if (endLevel > startLevel) { + return endHidden; + } + return startHidden; + } + + private int findColInfoIdx(int columnValue, int fromColInfoIdx) { + CTCols cols = worksheet.getColsArray(0); + + if (columnValue < 0) { + throw new IllegalArgumentException( + "column parameter out of range: " + columnValue); + } + if (fromColInfoIdx < 0) { + throw new IllegalArgumentException( + "fromIdx parameter out of range: " + fromColInfoIdx); + } + + for (int k = fromColInfoIdx; k < cols.sizeOfColArray(); k++) { + CTCol ci = cols.getColArray(k); + + if (containsColumn(ci, columnValue)) { + return k; + } + + if (ci.getMin() > fromColInfoIdx) { + break; + } + + } + return -1; + } + + private boolean containsColumn(CTCol col, int columnIndex) { + return col.getMin() <= columnIndex && columnIndex <= col.getMax(); + } + + /** + * 'Collapsed' state is stored in a single column col info record + * immediately after the outline group + * + * @param idx + * @return a boolean represented if the column is collapsed + */ + private boolean isColumnGroupCollapsed(int idx) { + CTCols cols = worksheet.getColsArray(0); + int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup(idx); + int nextColInfoIx = endOfOutlineGroupIdx + 1; + if (nextColInfoIx >= cols.sizeOfColArray()) { + return false; + } + CTCol nextColInfo = cols.getColArray(nextColInfoIx); + + CTCol col = cols.getColArray(endOfOutlineGroupIdx); + if (!isAdjacentBefore(col, nextColInfo)) { + return false; + } + + return nextColInfo.getCollapsed(); + } + + /** * Get the visibility state for a given column. * * @param columnIndex - the column to get (0-based) @@ -1327,11 +1693,164 @@ opts.setHorizontalCentered(value); } + /** - * Whether the output is vertically centered on the page. - * - * @param value true to vertically center, false otherwise. + * group the row It is possible for collapsed to be false and yet still have + * the rows in question hidden. This can be achieved by having a lower + * outline level collapsed, thus hiding all the child rows. Note that in + * this case, if the lowest level were expanded, the middle level would + * remain collapsed. + * + * @param rowIndex - + * the row involved + * @param collapse - + * boolean value for collapse */ + public void setRowGroupCollapsed(int rowIndex, boolean collapse) { + if (collapse) { + collapseRow(rowIndex); + } else { + expandRow(rowIndex); + } + } + + private void collapseRow(int rowIndex) { + XSSFRow row = getRow(rowIndex - 1); + if (row != null) { + int startRow = findStartOfRowOutlineGroup(rowIndex - 1); + + // Hide all the columns until the end of the group + int lastRow = writeHidden(row, startRow, true); + if (getRow(lastRow + 1) != null) { + getRow(lastRow + 1).getCTRow().setCollapsed(true); + } else { + XSSFRow newRow = createRow(lastRow + 1); + newRow.getCTRow().setCollapsed(true); + } + } + } + + private int findStartOfRowOutlineGroup(int rowIndex) { + // Find the start of the group. + int level = getRow(rowIndex).getCTRow().getOutlineLevel(); + int currentRow = rowIndex; + while (getRow(currentRow) != null) { + if (getRow(currentRow).getCTRow().getOutlineLevel() < level) + return currentRow + 1; + currentRow--; + } + return currentRow + 1; + } + + private int writeHidden(XSSFRow xRow, int rowIndex, boolean hidden) { + int level = xRow.getCTRow().getOutlineLevel(); + for (Iterator it = rowIterator(); it.hasNext();) { + xRow = (XSSFRow) it.next(); + if (xRow.getCTRow().getOutlineLevel() >= level) { + xRow.getCTRow().setHidden(hidden); + rowIndex++; + } + + } + return rowIndex; + } + + private void expandRow(int rowNumber) { + int idx = rowNumber; + if (idx == -1) + return; + XSSFRow row = getRow(rowNumber - 1); + // If it is already expanded do nothing. + if (!row.getCTRow().isSetHidden()) + return; + + // Find the start of the group. + int startIdx = findStartOfRowOutlineGroup(idx - 1); + + // Find the end of the group. + int endIdx = findEndOfRowOutlineGroup(idx - 1); + + // expand: + // collapsed must be unset + // hidden bit gets unset _if_ surrounding groups are expanded you can + // determine + // this by looking at the hidden bit of the enclosing group. You will + // have + // to look at the start and the end of the current group to determine + // which + // is the enclosing group + // hidden bit only is altered for this outline level. ie. don't + // un-collapse contained groups + if (!isRowGroupHiddenByParent(idx - 1)) { + for (int i = startIdx; i < endIdx; i++) { + if (row.getCTRow().getOutlineLevel() == getRow(i).getCTRow() + .getOutlineLevel()) { + getRow(i).getCTRow().unsetHidden(); + } else if (!isRowGroupCollapsed(i)) { + getRow(i).getCTRow().unsetHidden(); + } + } + } + // Write collapse field + getRow(endIdx + 1).getCTRow().unsetCollapsed(); + } + + public int findEndOfRowOutlineGroup(int row) { + int level = getRow(row).getCTRow().getOutlineLevel(); + int currentRow; + for (currentRow = row; currentRow < getLastRowNum(); currentRow++) { + if (getRow(currentRow) == null + || getRow(currentRow).getCTRow().getOutlineLevel() < level) { + break; + } + } + return currentRow; + } + + private boolean isRowGroupHiddenByParent(int row) { + // Look out outline details of end + int endLevel; + boolean endHidden; + int endOfOutlineGroupIdx = findEndOfRowOutlineGroup(row); + if (getRow(endOfOutlineGroupIdx + 1) == null) { + endLevel = 0; + endHidden = false; + } else { + endLevel = (int) (getRow(endOfOutlineGroupIdx + 1).getCTRow() + .getOutlineLevel()); + endHidden = getRow(endOfOutlineGroupIdx + 1).getCTRow().getHidden(); + } + + // Look out outline details of start + int startLevel; + boolean startHidden; + int startOfOutlineGroupIdx = findStartOfRowOutlineGroup(row); + if (startOfOutlineGroupIdx - 1 < 0 + || getRow(startOfOutlineGroupIdx - 1) == null) { + startLevel = 0; + startHidden = false; + } else { + startLevel = getRow(startOfOutlineGroupIdx - 1).getCTRow() + .getOutlineLevel(); + startHidden = getRow(startOfOutlineGroupIdx - 1).getCTRow() + .getHidden(); + } + if (endLevel > startLevel) { + return endHidden; + } else { + return startHidden; + } + } + + private boolean isRowGroupCollapsed(int row) { + int collapseRow = findEndOfRowOutlineGroup(row) + 1; + if (getRow(collapseRow) == null) + return false; + else + return getRow(collapseRow).getCTRow().getCollapsed(); + } + + public void setVerticallyCenter(boolean value) { CTPrintOptions opts = worksheet.isSetPrintOptions() ? worksheet.getPrintOptions() : worksheet.addNewPrintOptions(); @@ -1339,12 +1858,14 @@ } /** - * Sets the zoom magnication for the sheet. The zoom is expressed as a - * fraction. For example to express a zoom of 75% use 3 for the numerator + * Sets the zoom magnication for the sheet. The zoom is expressed as a + * fraction. For example to express a zoom of 75% use 3 for the numerator * and 4 for the denominator. - * - * @param numerator The numerator for the zoom magnification. - * @param denominator The denominator for the zoom magnification. + * + * @param numerator + * The numerator for the zoom magnification. + * @param denominator + * The denominator for the zoom magnification. * @see #setZoom(int) */ public void setZoom(int numerator, int denominator) { @@ -1416,7 +1937,6 @@ if (!copyRowHeight) { row.setHeight((short)-1); } - if (resetOriginalRowHeight && getDefaultRowHeight() >= 0) { row.setHeight(getDefaultRowHeight()); } @@ -1425,6 +1945,9 @@ } else if (row.getRowNum() >= startRow && row.getRowNum() <= endRow) { row.setRowNum(row.getRowNum() + n); + + modifyCellReference((XSSFRow)row); + if (row.getFirstCellNum() > -1) { modifyCellReference((XSSFRow) row); } @@ -1437,15 +1960,20 @@ } - private void modifyCellReference(XSSFRow row) { - for (int i = row.getFirstCellNum(); i <= row.getLastCellNum(); i++) { - XSSFCell c = row.getCell(i); - if (c != null) { - c.modifyCellReference(row); - } - } + private void modifyCellReference(XSSFRow row){ + int firstCellNum=row.getFirstCellNum(); + //a row could have no cell + if(firstCellNum != -1){ + for(int i=firstCellNum;i<=row.getLastCellNum();i++){ + XSSFCell c=(XSSFCell)row.getCell(i); + if(c!=null){ + c.modifyCellReference(row); + } + } + } } - + + /** * Location of the top left visible cell Location of the top left visible cell in the bottom right * pane (when in Left-to-Right mode). @@ -1456,7 +1984,8 @@ public void showInPane(short toprow, short leftcol) { CellReference cellReference = new CellReference(toprow, leftcol); String cellRef = cellReference.formatAsString(); - getSheetTypeSheetView().setTopLeftCell(cellRef); + // getSheetTypeSheetView().setTopLeftCell(cellRef); + getPane().setTopLeftCell(cellRef); } public void ungroupColumn(short fromColumn, short toColumn) { Index: src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java =================================================================== --- src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java (revision 711264) +++ src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java (working copy) @@ -18,6 +18,7 @@ package org.apache.poi.xssf.usermodel; import java.io.File; +import java.io.FileOutputStream; import java.util.Iterator; import junit.framework.TestCase; import org.apache.poi.ss.usermodel.Cell; @@ -852,10 +853,133 @@ public void testSetColumnGroupCollapsed(){ + Workbook wb = new XSSFWorkbook(); + XSSFSheet sheet1 =(XSSFSheet) wb.createSheet(); + sheet1.groupColumn( (short)4, (short)7 ); + sheet1.groupColumn( (short)9, (short)12 ); + sheet1.groupColumn( (short)10, (short)11 ); + CTCols cols=sheet1.getCTWorksheet().getColsArray(0); + assertEquals(4,cols.sizeOfColArray()); + + // collapse columns - 1 + sheet1.setColumnGroupCollapsed( (short)5, true ); + assertEquals(5,cols.sizeOfColArray()); + assertEquals(true,cols.getColArray(0).isSetHidden()); + assertEquals(false,cols.getColArray(0).isSetCollapsed()); + + assertEquals(9,cols.getColArray(1).getMin()); + assertEquals(false,cols.getColArray(1).getHidden()); + //assertEquals(false,cols.getColArray(1).isSetHidden()); ERRORE why??????? + assertEquals(true,cols.getColArray(1).isSetCollapsed()); + + assertEquals(false,cols.getColArray(2).getHidden()); +// assertEquals(false,cols.getColArray(2).isSetHidden()); ERRORE + assertEquals(false,cols.getColArray(2).isSetCollapsed()); + + + // expande columns - 1 + sheet1.setColumnGroupCollapsed( (short)5, false ); + + assertEquals(5,cols.sizeOfColArray()); + assertEquals(false,cols.getColArray(0).getHidden()); + assertEquals(false,cols.getColArray(1).getHidden()); + assertEquals(false,cols.getColArray(1).isSetCollapsed()); + assertEquals(9,cols.getColArray(1).getMin()); + + + //collapse - 2 + sheet1.setColumnGroupCollapsed( (short)9, true ); + assertEquals(6,cols.sizeOfColArray()); + assertEquals(true,cols.getColArray(1).isSetHidden()); + assertEquals(false,cols.getColArray(1).isSetCollapsed()); + + assertEquals(10,cols.getColArray(2).getMin()); + assertEquals(false,cols.getColArray(5).getHidden()); + assertEquals(true,cols.getColArray(5).isSetCollapsed()); + + + //expand - 2 + sheet1.setColumnGroupCollapsed( (short)9, false ); + assertEquals(6,cols.sizeOfColArray()); + assertEquals(14,cols.getColArray(5).getMin()); + + assertEquals(false,cols.getColArray(0).getHidden()); + assertEquals(false,cols.getColArray(2).getHidden()); + assertEquals(false,cols.getColArray(2).isSetCollapsed()); + assertEquals(10,cols.getColArray(2).getMin()); + //outline level 2: the line under ==> collapsed==True + assertEquals(2,cols.getColArray(3).getOutlineLevel()); + assertEquals(true,cols.getColArray(4).isSetCollapsed()); + + //DOCUMENTARE MEGLIO IL DISCORSO DEL LIVELLO + //collapse - 3 + sheet1.setColumnGroupCollapsed( (short)10, true ); + assertEquals(6,cols.sizeOfColArray()); + assertEquals(true,cols.getColArray(3).getHidden()); + assertEquals(false,cols.getColArray(3).isSetCollapsed()); + assertEquals(false,cols.getColArray(4).getHidden()); + assertEquals(true,cols.getColArray(4).isSetCollapsed()); + + assertEquals(false,cols.getColArray(0).getHidden()); + assertEquals(false,cols.getColArray(0).isSetCollapsed()); + + + //expand - 3 + sheet1.setColumnGroupCollapsed( (short)10, false ); + assertEquals(6,cols.sizeOfColArray()); + assertEquals(false,cols.getColArray(0).getHidden()); + assertEquals(false,cols.getColArray(5).getHidden()); + assertEquals(false,cols.getColArray(4).isSetCollapsed()); + +//write out and give back + // Save and re-load + wb = XSSFTestDataSamples.writeOutAndReadBack(wb); + sheet1 = (XSSFSheet)wb.getSheetAt(0); + assertEquals(6,cols.sizeOfColArray()); + assertEquals(false,cols.getColArray(0).getHidden()); + assertEquals(false,cols.getColArray(5).getHidden()); + assertEquals(false,cols.getColArray(4).isSetCollapsed()); } + public void testSetRowGroupCollapsed(){ + Workbook wb = new XSSFWorkbook(); + XSSFSheet sheet1 = (XSSFSheet)wb.createSheet(); + sheet1.groupRow( 5, 14 ); + sheet1.groupRow( 7, 14 ); + sheet1.groupRow( 16, 19 ); + + assertEquals(14,sheet1.getPhysicalNumberOfRows()); + //collapsed + sheet1.setRowGroupCollapsed( 7, true ); + + assertEquals(false,sheet1.getRow(6).getCTRow().isSetCollapsed()); + assertEquals(true,sheet1.getRow(6).getCTRow().isSetHidden()); + assertEquals(true,sheet1.getRow(15).getCTRow().isSetCollapsed()); + assertEquals(false,sheet1.getRow(15).getCTRow().isSetHidden()); + assertEquals(false,sheet1.getRow(16).getCTRow().isSetCollapsed()); + assertEquals(false,sheet1.getRow(16).getCTRow().isSetHidden()); + + //expanded + sheet1.setRowGroupCollapsed( 7, false ); + assertEquals(false,sheet1.getRow(6).getCTRow().isSetCollapsed()); + assertEquals(false,sheet1.getRow(6).getCTRow().isSetHidden()); + assertEquals(false,sheet1.getRow(15).getCTRow().isSetCollapsed()); + assertEquals(false,sheet1.getRow(15).getCTRow().isSetHidden()); + + + // Save and re-load + wb = XSSFTestDataSamples.writeOutAndReadBack(wb); + sheet1 = (XSSFSheet)wb.getSheetAt(0); + + assertEquals(false,sheet1.getRow(6).getCTRow().isSetCollapsed()); + assertEquals(false,sheet1.getRow(6).getCTRow().isSetHidden()); + assertEquals(false,sheet1.getRow(15).getCTRow().isSetCollapsed()); + assertEquals(false,sheet1.getRow(15).getCTRow().isSetHidden()); + +} + public void testColumnWidthCompatibility() { Workbook wb1 = new HSSFWorkbook(); Workbook wb2 = new XSSFWorkbook();