blob: 758ec6ef72a5723ff1ef056487f6553e6c6883bf [file] [log] [blame]
/*******************************************************************************
* You may amend and distribute as you like, but don't remove this header!
*
* EPPlus provides server-side generation of Excel 2007/2010 spreadsheets.
* See http://www.codeplex.com/EPPlus for details.
*
* Copyright (C) 2011 Jan K�llman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
* If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
*
* All code and executables are provided "as is" with no warranty either express or implied.
* The author accepts no liability for any damage or loss of business that this product may cause.
*
* Code change notes:
*
* Author Change Date
* ******************************************************************************
* Jan K�llman Initial Release 2009-10-01
* Jan K�llman License changed GPL-->LGPL 2011-12-27
*******************************************************************************/
using System;
using System.Collections.Immutable;
using System.Xml;
namespace OfficeOpenXml;
/// <summary>
/// Represents the different view states of the worksheet
/// </summary>
public class ExcelWorksheetView : XmlHelper {
/// <summary>
/// The worksheet panes after a freeze or split.
/// </summary>
public class ExcelWorksheetPanes : XmlHelper {
private XmlElement _selectionNode;
internal ExcelWorksheetPanes(XmlNamespaceManager ns, XmlNode topNode)
: base(ns, topNode) {
if (topNode.Name == "selection") {
_selectionNode = topNode as XmlElement;
}
}
private const string _activeCellPath = "@activeCell";
/// <summary>
/// Set the active cell. Must be set within the SelectedRange.
/// </summary>
public string ActiveCell {
get {
string address = GetXmlNodeString(_activeCellPath);
if (address == "") {
return "A1";
}
return address;
}
set {
int toCol,
toRow;
if (_selectionNode == null) {
CreateSelectionElement();
}
ExcelCellBase.GetRowColFromAddress(
value,
out var fromRow,
out var fromCol,
out toRow,
out toCol);
SetXmlNodeString(_activeCellPath, value);
if (((XmlElement)TopNode).GetAttribute("sqref") == "") {
SelectedRange = ExcelCellBase.GetAddress(fromRow, fromCol);
}
//TODO:Add fix for out of range here
}
}
private void CreateSelectionElement() {
_selectionNode = TopNode.OwnerDocument.CreateElement("selection", ExcelPackage._schemaMain);
TopNode.AppendChild(_selectionNode);
TopNode = _selectionNode;
}
private const string _selectionRangePath = "@sqref";
/// <summary>
/// Selected Cells.Used in combination with ActiveCell
/// </summary>
public string SelectedRange {
get {
string address = GetXmlNodeString(_selectionRangePath);
if (address == "") {
return "A1";
}
return address;
}
set {
int toCol,
toRow;
if (_selectionNode == null) {
CreateSelectionElement();
}
ExcelCellBase.GetRowColFromAddress(
value,
out var fromRow,
out var fromCol,
out toRow,
out toCol);
SetXmlNodeString(_selectionRangePath, value);
if (((XmlElement)TopNode).GetAttribute("activeCell") == "") {
ActiveCell = ExcelCellBase.GetAddress(fromRow, fromCol);
}
//TODO:Add fix for out of range here
}
}
}
private readonly ExcelWorksheet _worksheet;
protected override ImmutableArray<string> SchemaNodeOrder { get; } = [
"sheetViews",
"sheetView",
"pane",
"selection",
];
/// <summary>
/// Creates a new ExcelWorksheetView which provides access to all the view states of the worksheet.
/// </summary>
/// <param name="ns"></param>
/// <param name="node"></param>
/// <param name="xlWorksheet"></param>
internal ExcelWorksheetView(XmlNamespaceManager ns, XmlNode node, ExcelWorksheet xlWorksheet)
: base(ns, node) {
_worksheet = xlWorksheet;
Panes = LoadPanes();
}
private ExcelWorksheetPanes[] LoadPanes() {
XmlNodeList nodes = TopNode.SelectNodes("//d:selection", NameSpaceManager);
if (nodes.Count == 0) {
return new[] { new ExcelWorksheetPanes(NameSpaceManager, TopNode) };
}
ExcelWorksheetPanes[] panes = new ExcelWorksheetPanes[nodes.Count];
int i = 0;
foreach (XmlElement elem in nodes) {
panes[i++] = new(NameSpaceManager, elem);
}
return panes;
}
/// <summary>
/// Returns a reference to the sheetView element
/// </summary>
protected internal XmlElement SheetViewElement => (XmlElement)TopNode;
/// <summary>
/// The active cell.
/// </summary>
public string ActiveCell {
get => Panes[Panes.GetUpperBound(0)].ActiveCell;
set => Panes[Panes.GetUpperBound(0)].ActiveCell = value;
}
/// <summary>
/// Selected Cells in the worksheet.Used in combination with ActiveCell
/// </summary>
public string SelectedRange {
get => Panes[Panes.GetUpperBound(0)].SelectedRange;
set => Panes[Panes.GetUpperBound(0)].SelectedRange = value;
}
/// <summary>
/// Indicates if the worksheet is selected within the workbook
/// </summary>
public bool TabSelected {
get => GetXmlNodeBool("@tabSelected");
set {
if (value) {
// // ensure no other worksheet has its tabSelected attribute set to 1
foreach (ExcelWorksheet sheet in _worksheet.Workbook.Worksheets) {
sheet.View.TabSelected = false;
}
SheetViewElement.SetAttribute("tabSelected", "1");
XmlElement bookView =
_worksheet.Workbook.WorkbookXml.SelectSingleNode(
"//d:workbookView",
_worksheet.NameSpaceManager) as XmlElement;
if (bookView != null) {
bookView.SetAttribute("activeTab", (_worksheet.PositionID - 1).ToString());
}
} else {
SetXmlNodeString("@tabSelected", "0");
}
}
}
/// <summary>
/// Sets the view mode of the worksheet to pagelayout
/// </summary>
public bool PageLayoutView {
get => GetXmlNodeString("@view") == "pageLayout";
set {
if (value) {
SetXmlNodeString("@view", "pageLayout");
} else {
SheetViewElement.RemoveAttribute("view");
}
}
}
/// <summary>
/// Sets the view mode of the worksheet to pagebreak
/// </summary>
public bool PageBreakView {
get => GetXmlNodeString("@view") == "pageBreakPreview";
set {
if (value) {
SetXmlNodeString("@view", "pageBreakPreview");
} else {
SheetViewElement.RemoveAttribute("view");
}
}
}
/// <summary>
/// Show gridlines in the worksheet
/// </summary>
public bool ShowGridLines {
get => GetXmlNodeBool("@showGridLines");
set => SetXmlNodeString("@showGridLines", value ? "1" : "0");
}
/// <summary>
/// Show the Column/Row headers (containg column letters and row numbers)
/// </summary>
public bool ShowHeaders {
get => GetXmlNodeBool("@showRowColHeaders");
set => SetXmlNodeString("@showRowColHeaders", value ? "1" : "0");
}
/// <summary>
/// Window zoom magnification for current view representing percent values.
/// </summary>
public int ZoomScale {
get => GetXmlNodeInt("@zoomScale");
set {
if (value < 10 || value > 400) {
throw new ArgumentOutOfRangeException("Zoome scale out of range (10-400)");
}
SetXmlNodeString("@zoomScale", value.ToString());
}
}
/// <summary>
/// Flag indicating whether the sheet is in 'right to left' display mode. When in this mode,Column A is on the far right, Column B ;is one column left of Column A, and so on. Also,information in cells is displayed in the Right to Left format.
/// </summary>
public bool RightToLeft {
get => GetXmlNodeBool("@rightToLeft");
set => SetXmlNodeString("@rightToLeft", value ? "1" : "0");
}
internal bool WindowProtection {
get => GetXmlNodeBool("@windowProtection", false);
set => SetXmlNodeBool("@windowProtection", value, false);
}
/// <summary>
/// Reference to the panes
/// </summary>
public ExcelWorksheetPanes[] Panes { get; internal set; }
private readonly string _paneNodePath = "d:pane";
private readonly string _selectionNodePath = "d:selection";
/// <summary>
/// Freeze the columns/rows to left and above the cell
/// </summary>
/// <param name="row"></param>
/// <param name="column"></param>
public void FreezePanes(int row, int column) {
//TODO:fix this method to handle splits as well.
if (row == 1 && column == 1) {
UnFreezePanes();
}
string sqRef = SelectedRange,
activeCell = ActiveCell;
XmlElement paneNode = TopNode.SelectSingleNode(_paneNodePath, NameSpaceManager) as XmlElement;
if (paneNode == null) {
CreateNode(_paneNodePath);
paneNode = TopNode.SelectSingleNode(_paneNodePath, NameSpaceManager) as XmlElement;
}
paneNode.RemoveAll(); //Clear all attributes
if (column > 1) {
paneNode.SetAttribute("xSplit", (column - 1).ToString());
}
if (row > 1) {
paneNode.SetAttribute("ySplit", (row - 1).ToString());
}
paneNode.SetAttribute("topLeftCell", ExcelCellBase.GetAddress(row, column));
paneNode.SetAttribute("state", "frozen");
RemoveSelection();
if (row > 1 && column == 1) {
paneNode.SetAttribute("activePane", "bottomLeft");
XmlElement sel = TopNode.OwnerDocument.CreateElement("selection", ExcelPackage._schemaMain);
sel.SetAttribute("pane", "bottomLeft");
if (activeCell != "") {
sel.SetAttribute("activeCell", activeCell);
}
if (sqRef != "") {
sel.SetAttribute("sqref", sqRef);
}
sel.SetAttribute("sqref", sqRef);
TopNode.InsertAfter(sel, paneNode);
} else if (column > 1 && row == 1) {
paneNode.SetAttribute("activePane", "topRight");
XmlElement sel = TopNode.OwnerDocument.CreateElement("selection", ExcelPackage._schemaMain);
sel.SetAttribute("pane", "topRight");
if (activeCell != "") {
sel.SetAttribute("activeCell", activeCell);
}
if (sqRef != "") {
sel.SetAttribute("sqref", sqRef);
}
TopNode.InsertAfter(sel, paneNode);
} else {
paneNode.SetAttribute("activePane", "bottomRight");
XmlElement sel1 = TopNode.OwnerDocument.CreateElement("selection", ExcelPackage._schemaMain);
sel1.SetAttribute("pane", "topRight");
string cell = ExcelCellBase.GetAddress(1, column);
sel1.SetAttribute("activeCell", cell);
sel1.SetAttribute("sqref", cell);
paneNode.ParentNode.InsertAfter(sel1, paneNode);
XmlElement sel2 = TopNode.OwnerDocument.CreateElement("selection", ExcelPackage._schemaMain);
cell = ExcelCellBase.GetAddress(row, 1);
sel2.SetAttribute("pane", "bottomLeft");
sel2.SetAttribute("activeCell", cell);
sel2.SetAttribute("sqref", cell);
sel1.ParentNode.InsertAfter(sel2, sel1);
XmlElement sel3 = TopNode.OwnerDocument.CreateElement("selection", ExcelPackage._schemaMain);
sel3.SetAttribute("pane", "bottomRight");
if (activeCell != "") {
sel3.SetAttribute("activeCell", activeCell);
}
if (sqRef != "") {
sel3.SetAttribute("sqref", sqRef);
}
sel2.ParentNode.InsertAfter(sel3, sel2);
}
Panes = LoadPanes();
}
private void RemoveSelection() {
//Find selection nodes and remove them
XmlNodeList selections = TopNode.SelectNodes(_selectionNodePath, NameSpaceManager);
foreach (XmlNode sel in selections) {
sel.ParentNode.RemoveChild(sel);
}
}
/// <summary>
/// Unlock all rows and columns to scroll freely
/// /// </summary>
public void UnFreezePanes() {
string sqRef = SelectedRange,
activeCell = ActiveCell;
XmlElement paneNode = TopNode.SelectSingleNode(_paneNodePath, NameSpaceManager) as XmlElement;
if (paneNode != null) {
paneNode.ParentNode.RemoveChild(paneNode);
}
RemoveSelection();
Panes = LoadPanes();
SelectedRange = sqRef;
ActiveCell = activeCell;
}
}