true if this sheet is currently selected
*/
public boolean isSelected() {
return getSheet().getWindowTwo().getSelected();
}
/**
* Sets whether sheet is selected.
* @param sel Whether to select the sheet or deselect the sheet.
*/
public void setSelected( boolean sel )
{
getSheet().getWindowTwo().setSelected(sel);
}
/**
* @return true if this sheet is currently focused
*/
public boolean isActive() {
return getSheet().getWindowTwo().isActive();
}
/**
* Sets whether sheet is selected.
* @param sel Whether to select the sheet or deselect the sheet.
*/
public void setActive(boolean sel )
{
getSheet().getWindowTwo().setActive(sel);
}
/**
* Gets the size of the margin in inches.
* @param margin which margin to get
* @return the size of the margin
*/
public double getMargin( short margin )
{
return sheet.getPageSettings().getMargin( margin );
}
/**
* Sets the size of the margin in inches.
* @param margin which margin to get
* @param size the size of the margin
*/
public void setMargin( short margin, double size )
{
sheet.getPageSettings().setMargin( margin, size );
}
/**
* Answer whether protection is enabled or disabled
* @return true => protection enabled; false => protection disabled
*/
public boolean getProtect() {
return getSheet().isProtected()[0];
}
/**
* @return hashed password
*/
public short getPassword() {
return getSheet().getPassword().getPassword();
}
/**
* Answer whether object protection is enabled or disabled
* @return true => protection enabled; false => protection disabled
*/
public boolean getObjectProtect() {
return getSheet().isProtected()[1];
}
/**
* Answer whether scenario protection is enabled or disabled
* @return true => protection enabled; false => protection disabled
*/
public boolean getScenarioProtect() {
return getSheet().isProtected()[2];
}
/**
* Sets the protection on enabled or disabled
* @param protect true => protection enabled; false => protection disabled
* @deprecated use protectSheet(String, boolean, boolean)
*/
public void setProtect(boolean protect) {
getSheet().getProtect().setProtect(protect);
}
/**
* Sets the protection enabled as well as the password
* @param password to set for protection
*/
public void protectSheet(String password) {
getSheet().protectSheet(password, true, true); //protect objs&scenarios(normal)
}
/**
* 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.
*/
public void setZoom( int numerator, int denominator)
{
if (numerator < 1 || numerator > 65535)
throw new IllegalArgumentException("Numerator must be greater than 1 and less than 65536");
if (denominator < 1 || denominator > 65535)
throw new IllegalArgumentException("Denominator must be greater than 1 and less than 65536");
SCLRecord sclRecord = new SCLRecord();
sclRecord.setNumerator((short)numerator);
sclRecord.setDenominator((short)denominator);
getSheet().setSCLRecord(sclRecord);
}
/**
* The top row in the visible view when the sheet is
* first viewed after opening it in a viewer
* @return short indicating the rownum (0 based) of the top row
*/
public short getTopRow()
{
return sheet.getTopRow();
}
/**
* The left col in the visible view when the sheet is
* first viewed after opening it in a viewer
* @return short indicating the rownum (0 based) of the top row
*/
public short getLeftCol()
{
return sheet.getLeftCol();
}
/**
* Sets desktop window pane display area, when the
* file is first opened in a viewer.
* @param toprow the top row to show in desktop window pane
* @param leftcol the left column to show in desktop window pane
*/
public void showInPane(short toprow, short leftcol){
this.sheet.setTopRow(toprow);
this.sheet.setLeftCol(leftcol);
}
/**
* Shifts the merged regions left or right depending on mode
* * TODO: MODE , this is only row specific * @param startRow * @param endRow * @param n * @param isRow */ protected void shiftMerged(int startRow, int endRow, int n, boolean isRow) { List shiftedRegions = new ArrayList(); //move merged regions completely if they fall within the new region boundaries when they are shifted for (int i = 0; i < getNumMergedRegions(); i++) { CellRangeAddress merged = getMergedRegion(i); boolean inStart= (merged.getFirstRow() >= startRow || merged.getLastRow() >= startRow); boolean inEnd = (merged.getFirstRow() <= endRow || merged.getLastRow() <= endRow); //don't check if it's not within the shifted area if (!inStart || !inEnd) { continue; } //only shift if the region outside the shifted rows is not merged too if (!containsCell(merged, startRow-1, 0) && !containsCell(merged, endRow+1, 0)){ merged.setFirstRow(merged.getFirstRow()+n); merged.setLastRow(merged.getLastRow()+n); //have to remove/add it back shiftedRegions.add(merged); removeMergedRegion(i); i = i -1; // we have to back up now since we removed one } } //read so it doesn't get shifted again Iterator iterator = shiftedRegions.iterator(); while (iterator.hasNext()) { CellRangeAddress region = (CellRangeAddress)iterator.next(); this.addMergedRegion(region); } } private static boolean containsCell(CellRangeAddress cr, int rowIx, int colIx) { if (cr.getFirstRow() <= rowIx && cr.getLastRow() >= rowIx && cr.getFirstColumn() <= colIx && cr.getLastColumn() >= colIx) { return true; } return false; } /** * Shifts rows between startRow and endRow n number of rows. * If you use a negative number, it will shift rows up. * Code ensures that rows don't wrap around. * * Calls shiftRows(startRow, endRow, n, false, false); * *
* Additionally shifts merged regions that are completely defined in these * rows (ie. merged 2 cells on a row to be shifted). * @param startRow the row to start shifting * @param endRow the row to end shifting * @param n the number of rows to shift */ public void shiftRows( int startRow, int endRow, int n ) { shiftRows(startRow, endRow, n, false, false); } /** * Shifts rows between startRow and endRow n number of rows. * If you use a negative number, it will shift rows up. * Code ensures that rows don't wrap around * *
* Additionally shifts merged regions that are completely defined in these * rows (ie. merged 2 cells on a row to be shifted). *
* TODO Might want to add bounds checking here * @param startRow the row to start shifting * @param endRow the row to end shifting * @param n the number of rows to shift * @param copyRowHeight whether to copy the row height during the shift * @param resetOriginalRowHeight whether to set the original row's height to the default */ public void shiftRows( int startRow, int endRow, int n, boolean copyRowHeight, boolean resetOriginalRowHeight) { shiftRows(startRow, endRow, n, copyRowHeight, resetOriginalRowHeight, true); } /** * Shifts rows between startRow and endRow n number of rows. * If you use a negative number, it will shift rows up. * Code ensures that rows don't wrap around * *
* Additionally shifts merged regions that are completely defined in these * rows (ie. merged 2 cells on a row to be shifted). *
* TODO Might want to add bounds checking here
* @param startRow the row to start shifting
* @param endRow the row to end shifting
* @param n the number of rows to shift
* @param copyRowHeight whether to copy the row height during the shift
* @param resetOriginalRowHeight whether to set the original row's height to the default
* @param moveComments whether to move comments at the same time as the cells they are attached to
*/
public void shiftRows(int startRow, int endRow, int n,
boolean copyRowHeight, boolean resetOriginalRowHeight, boolean moveComments) {
int s, inc;
if (n < 0) {
s = startRow;
inc = 1;
} else {
s = endRow;
inc = -1;
}
shiftMerged(startRow, endRow, n, true);
sheet.getPageSettings().shiftRowBreaks(startRow, endRow, n);
for ( int rowNum = s; rowNum >= startRow && rowNum <= endRow && rowNum >= 0 && rowNum < 65536; rowNum += inc ) {
HSSFRow row = getRow( rowNum );
HSSFRow row2Replace = getRow( rowNum + n );
if ( row2Replace == null )
row2Replace = createRow( rowNum + n );
// Remove all the old cells from the row we'll
// be writing too, before we start overwriting
// any cells. This avoids issues with cells
// changing type, and records not being correctly
// overwritten
row2Replace.removeAllCells();
// If this row doesn't exist, nothing needs to
// be done for the now empty destination row
if (row == null) continue; // Nothing to do for this row
// Fetch the first and last columns of the
// row now, so we still have them to hand
// once we start removing cells
short firstCol = row.getFirstCellNum();
short lastCol = row.getLastCellNum();
// Fix up row heights if required
if (copyRowHeight) {
row2Replace.setHeight(row.getHeight());
}
if (resetOriginalRowHeight) {
row.setHeight((short)0xff);
}
// Copy each cell from the source row to
// the destination row
for(Iterator cells = row.cellIterator(); cells.hasNext(); ) {
HSSFCell cell = (HSSFCell)cells.next();
row.removeCell( cell );
CellValueRecordInterface cellRecord = cell.getCellValueRecord();
cellRecord.setRow( rowNum + n );
row2Replace.createCellFromRecord( cellRecord );
sheet.addValueRecord( rowNum + n, cellRecord );
}
// Now zap all the cells in the source row
row.removeAllCells();
// Move comments from the source row to the
// destination row. Note that comments can
// exist for cells which are null
if(moveComments) {
for( short col = firstCol; col <= lastCol; col++ ) {
HSSFComment comment = getCellComment(rowNum, col);
if (comment != null) {
comment.setRow(rowNum + n);
}
}
}
}
if ( endRow == lastrow || endRow + n > lastrow ) lastrow = Math.min( endRow + n, 65535 );
if ( startRow == firstrow || startRow + n < firstrow ) firstrow = Math.max( startRow + n, 0 );
// Update any formulas on this sheet that point to
// rows which have been moved
int sheetIndex = workbook.getSheetIndex(this);
short externSheetIndex = book.checkExternSheet(sheetIndex);
FormulaShifter shifter = FormulaShifter.createForRowShift(externSheetIndex, startRow, endRow, n);
sheet.getRowsAggregate().updateFormulasAfterRowShift(shifter, externSheetIndex);
int nSheets = workbook.getNumberOfSheets();
for(int i=0; itrue if there is a page break at the indicated row
*/
public boolean isRowBroken(int row) {
return sheet.getPageSettings().isRowBroken(row);
}
/**
* Removes the page break at the indicated row
*/
public void removeRowBreak(int row) {
sheet.getPageSettings().removeRowBreak(row);
}
/**
* @return row indexes of all the horizontal page breaks, never null
*/
public int[] getRowBreaks(){
//we can probably cache this information, but this should be a sparsely used function
return sheet.getPageSettings().getRowBreaks();
}
/**
* @return column indexes of all the vertical page breaks, never null
*/
public int[] getColumnBreaks(){
//we can probably cache this information, but this should be a sparsely used function
return sheet.getPageSettings().getColumnBreaks();
}
/**
* Sets a page break at the indicated column
* @param column
*/
public void setColumnBreak(short column) {
validateColumn(column);
sheet.getPageSettings().setColumnBreak(column, (short)0, (short)65535);
}
/**
* Determines if there is a page break at the indicated column
* @param column FIXME: Document this!
* @return FIXME: Document this!
*/
public boolean isColumnBroken(short column) {
return sheet.getPageSettings().isColumnBroken(column);
}
/**
* Removes a page break at the indicated column
* @param column
*/
public void removeColumnBreak(short column) {
sheet.getPageSettings().removeColumnBreak(column);
}
/**
* Runs a bounds check for row numbers
* @param row
*/
protected void validateRow(int row) {
if (row > 65535) throw new IllegalArgumentException("Maximum row number is 65535");
if (row < 0) throw new IllegalArgumentException("Minumum row number is 0");
}
/**
* Runs a bounds check for column numbers
* @param column
*/
protected void validateColumn(short column) {
if (column > 255) throw new IllegalArgumentException("Maximum column number is 255");
if (column < 0) throw new IllegalArgumentException("Minimum column number is 0");
}
/**
* Aggregates the drawing records and dumps the escher record hierarchy
* to the standard output.
*/
public void dumpDrawingRecords(boolean fat)
{
sheet.aggregateDrawingRecords(book.getDrawingManager(), false);
EscherAggregate r = (EscherAggregate) getSheet().findFirstRecordBySid(EscherAggregate.sid);
List escherRecords = r.getEscherRecords();
PrintWriter w = new PrintWriter(System.out);
for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); )
{
EscherRecord escherRecord = (EscherRecord) iterator.next();
if (fat)
System.out.println(escherRecord.toString());
else
escherRecord.display(w, 0);
}
w.flush();
}
/**
* Creates the top-level drawing patriarch. This will have
* the effect of removing any existing drawings on this
* sheet.
* This may then be used to add graphics or charts
* @return The new patriarch.
*/
public HSSFPatriarch createDrawingPatriarch()
{
// Create the drawing group if it doesn't already exist.
book.createDrawingGroup();
sheet.aggregateDrawingRecords(book.getDrawingManager(), true);
EscherAggregate agg = (EscherAggregate) sheet.findFirstRecordBySid(EscherAggregate.sid);
HSSFPatriarch patriarch = new HSSFPatriarch(this, agg);
agg.clear(); // Initially the behaviour will be to clear out any existing shapes in the sheet when
// creating a new patriarch.
agg.setPatriarch(patriarch);
return patriarch;
}
/**
* Returns the agregate escher records for this sheet,
* it there is one.
* WARNING - calling this will trigger a parsing of the
* associated escher records. Any that aren't supported
* (such as charts and complex drawing types) will almost
* certainly be lost or corrupted when written out.
*/
public EscherAggregate getDrawingEscherAggregate() {
book.findDrawingGroup();
// If there's now no drawing manager, then there's
// no drawing escher records on the workbook
if(book.getDrawingManager() == null) {
return null;
}
int found = sheet.aggregateDrawingRecords(
book.getDrawingManager(), false
);
if(found == -1) {
// Workbook has drawing stuff, but this sheet doesn't
return null;
}
// Grab our aggregate record, and wire it up
EscherAggregate agg = (EscherAggregate) sheet.findFirstRecordBySid(EscherAggregate.sid);
return agg;
}
/**
* Returns the top-level drawing patriach, if there is
* one.
* This will hold any graphics or charts for the sheet.
* WARNING - calling this will trigger a parsing of the
* associated escher records. Any that aren't supported
* (such as charts and complex drawing types) will almost
* certainly be lost or corrupted when written out. Only
* use this with simple drawings, otherwise call
* {@link HSSFSheet#createDrawingPatriarch()} and
* start from scratch!
*/
public HSSFPatriarch getDrawingPatriarch() {
EscherAggregate agg = getDrawingEscherAggregate();
if(agg == null) return null;
HSSFPatriarch patriarch = new HSSFPatriarch(this, agg);
agg.setPatriarch(patriarch);
// Have it process the records into high level objects
// as best it can do (this step may eat anything
// that isn't supported, you were warned...)
agg.convertRecordsToUserModel();
// Return what we could cope with
return patriarch;
}
/**
* @deprecated (Sep 2008) use {@link #setColumnGroupCollapsed(int, boolean)}
*/
public void setColumnGroupCollapsed(short columnNumber, boolean collapsed) {
setColumnGroupCollapsed(columnNumber & 0xFFFF, collapsed);
}
/**
* @deprecated (Sep 2008) use {@link #groupColumn(int, int)}
*/
public void groupColumn(short fromColumn, short toColumn) {
groupColumn(fromColumn & 0xFFFF, toColumn & 0xFFFF);
}
/**
* @deprecated (Sep 2008) use {@link #ungroupColumn(int, int)}
*/
public void ungroupColumn(short fromColumn, short toColumn) {
ungroupColumn(fromColumn & 0xFFFF, toColumn & 0xFFFF);
}
/**
* Expands or collapses a column group.
*
* @param columnNumber One of the columns in the group.
* @param collapsed true = collapse group, false = expand group.
*/
public void setColumnGroupCollapsed(int columnNumber, boolean collapsed) {
sheet.setColumnGroupCollapsed(columnNumber, collapsed);
}
/**
* Create an outline for the provided column range.
*
* @param fromColumn beginning of the column range.
* @param toColumn end of the column range.
*/
public void groupColumn(int fromColumn, int toColumn) {
sheet.groupColumnRange(fromColumn, toColumn, true);
}
public void ungroupColumn(int fromColumn, int toColumn) {
sheet.groupColumnRange(fromColumn, toColumn, false);
}
public void groupRow(int fromRow, int toRow)
{
sheet.groupRowRange( fromRow, toRow, true );
}
public void ungroupRow(int fromRow, int toRow)
{
sheet.groupRowRange( fromRow, toRow, false );
}
public void setRowGroupCollapsed(int rowIndex, boolean collapse) {
if (collapse) {
sheet.getRowsAggregate().collapseRow(rowIndex);
} else {
sheet.getRowsAggregate().expandRow(rowIndex);
}
}
/**
* Sets the default column style for a given column. POI will only apply this style to new cells added to the sheet.
*
* @param column the column index
* @param style the style to set
*/
public void setDefaultColumnStyle(short column, CellStyle style) {
sheet.setColumn(column, new Short(((HSSFCellStyle) style).getIndex()), null, null, null, null);
}
/**
* Adjusts the column width to fit the contents.
*
* This process can be relatively slow on large sheets, so this should
* normally only be called once per column, at the end of your
* processing.
*
* @param column the column index
*/
public void autoSizeColumn(short column) {
autoSizeColumn(column, false);
}
/**
* Adjusts the column width to fit the contents.
*
* This process can be relatively slow on large sheets, so this should
* normally only be called once per column, at the end of your
* processing.
*
* You can specify whether the content of merged cells should be considered or ignored.
* Default is to ignore merged cells.
*
* @param column the column index
* @param useMergedCells whether to use the contents of merged cells when calculating the width of the column
*/
public void autoSizeColumn(short column, boolean useMergedCells) {
AttributedString str;
TextLayout layout;
/**
* Excel measures columns in units of 1/256th of a character width
* but the docs say nothing about what particular character is used.
* '0' looks to be a good choice.
*/
char defaultChar = '0';
/**
* This is the multiple that the font height is scaled by when determining the
* boundary of rotated text.
*/
double fontHeightMultiple = 2.0;
FontRenderContext frc = new FontRenderContext(null, true, true);
HSSFWorkbook wb = new HSSFWorkbook(book);
HSSFFont defaultFont = wb.getFontAt((short) 0);
str = new AttributedString("" + defaultChar);
copyAttributes(defaultFont, str, 0, 1);
layout = new TextLayout(str.getIterator(), frc);
int defaultCharWidth = (int)layout.getAdvance();
double width = -1;
rows:
for (Iterator it = rowIterator(); it.hasNext();) {
HSSFRow row = (HSSFRow) it.next();
HSSFCell cell = row.getCell(column);
if (cell == null) {
continue;
}
int colspan = 1;
for (int i = 0 ; i < getNumMergedRegions(); i++) {
CellRangeAddress region = getMergedRegion(i);
if (containsCell(region, row.getRowNum(), column)) {
if (!useMergedCells) {
// If we're not using merged cells, skip this one and move on to the next.
continue rows;
}
cell = row.getCell(region.getFirstColumn());
colspan = 1 + region.getLastColumn() - region.getFirstColumn();
}
}
HSSFCellStyle style = cell.getCellStyle();
HSSFFont font = wb.getFontAt(style.getFontIndex());
if (cell.getCellType() == HSSFCell.CELL_TYPE_STRING) {
HSSFRichTextString rt = cell.getRichStringCellValue();
String[] lines = rt.getString().split("\\n");
for (int i = 0; i < lines.length; i++) {
String txt = lines[i] + defaultChar;
str = new AttributedString(txt);
copyAttributes(font, str, 0, txt.length());
if (rt.numFormattingRuns() > 0) {
for (int j = 0; j < lines[i].length(); j++) {
int idx = rt.getFontAtIndex(j);
if (idx != 0) {
HSSFFont fnt = wb.getFontAt((short) idx);
copyAttributes(fnt, str, j, j + 1);
}
}
}
layout = new TextLayout(str.getIterator(), frc);
if(style.getRotation() != 0){
/*
* Transform the text using a scale so that it's height is increased by a multiple of the leading,
* and then rotate the text before computing the bounds. The scale results in some whitespace around
* the unrotated top and bottom of the text that normally wouldn't be present if unscaled, but
* is added by the standard Excel autosize.
*/
AffineTransform trans = new AffineTransform();
trans.concatenate(AffineTransform.getRotateInstance(style.getRotation()*2.0*Math.PI/360.0));
trans.concatenate(
AffineTransform.getScaleInstance(1, fontHeightMultiple)
);
width = Math.max(width, ((layout.getOutline(trans).getBounds().getWidth() / colspan) / defaultCharWidth) + cell.getCellStyle().getIndention());
} else {
width = Math.max(width, ((layout.getBounds().getWidth() / colspan) / defaultCharWidth) + cell.getCellStyle().getIndention());
}
}
} else {
String sval = null;
if (cell.getCellType() == HSSFCell.CELL_TYPE_NUMERIC) {
String format = style.getDataFormatString().replaceAll("\"", "");
double value = cell.getNumericCellValue();
try {
NumberFormat fmt;
if ("General".equals(format))
sval = "" + value;
else
{
fmt = new DecimalFormat(format);
sval = fmt.format(value);
}
} catch (Exception e) {
sval = "" + value;
}
} else if (cell.getCellType() == HSSFCell.CELL_TYPE_BOOLEAN) {
sval = String.valueOf(cell.getBooleanCellValue());
}
if(sval != null) {
String txt = sval + defaultChar;
str = new AttributedString(txt);
copyAttributes(font, str, 0, txt.length());
layout = new TextLayout(str.getIterator(), frc);
if(style.getRotation() != 0){
/*
* Transform the text using a scale so that it's height is increased by a multiple of the leading,
* and then rotate the text before computing the bounds. The scale results in some whitespace around
* the unrotated top and bottom of the text that normally wouldn't be present if unscaled, but
* is added by the standard Excel autosize.
*/
AffineTransform trans = new AffineTransform();
trans.concatenate(AffineTransform.getRotateInstance(style.getRotation()*2.0*Math.PI/360.0));
trans.concatenate(
AffineTransform.getScaleInstance(1, fontHeightMultiple)
);
width = Math.max(width, ((layout.getOutline(trans).getBounds().getWidth() / colspan) / defaultCharWidth) + cell.getCellStyle().getIndention());
} else {
width = Math.max(width, ((layout.getBounds().getWidth() / colspan) / defaultCharWidth) + cell.getCellStyle().getIndention());
}
}
}
}
if (width != -1) {
width *= 256;
if (width > Short.MAX_VALUE) { //width can be bigger that Short.MAX_VALUE!
width = Short.MAX_VALUE;
}
sheet.setColumnWidth(column, (short) (width));
}
}
/**
* Copy text attributes from the supplied HSSFFont to Java2D AttributedString
*/
private void copyAttributes(HSSFFont font, AttributedString str, int startIdx, int endIdx) {
str.addAttribute(TextAttribute.FAMILY, font.getFontName(), startIdx, endIdx);
str.addAttribute(TextAttribute.SIZE, new Float(font.getFontHeightInPoints()));
if (font.getBoldweight() == HSSFFont.BOLDWEIGHT_BOLD) str.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, startIdx, endIdx);
if (font.getItalic() ) str.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE, startIdx, endIdx);
if (font.getUnderline() == HSSFFont.U_SINGLE ) str.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, startIdx, endIdx);
}
/**
* Returns cell comment for the specified row and column
*
* @return cell comment or null if not found
*/
public HSSFComment getCellComment(int row, int column) {
// Don't call findCellComment directly, otherwise
// two calls to this method will result in two
// new HSSFComment instances, which is bad
HSSFRow r = getRow(row);
if(r != null) {
HSSFCell c = r.getCell(column);
if(c != null) {
return c.getCellComment();
} else {
// No cell, so you will get new
// objects every time, sorry...
return HSSFCell.findCellComment(sheet, row, column);
}
}
return null;
}
public HSSFSheetConditionalFormatting getSheetConditionalFormatting() {
return new HSSFSheetConditionalFormatting(workbook, sheet);
}
}